Cleaning up and Debugging (Exposed by AcessNative mostly)
[tpg/acess2.git] / Kernel / arch / x86 / lib.c
1 /*
2  * AcessOS Microkernel Version
3  * lib.c
4  */
5 #include <acess.h>
6 #include <threads_int.h>
7
8 #define TRACE_LOCKS     0
9
10 #define DEBUG_TO_E9     1
11 #define DEBUG_TO_SERIAL 1
12 #define SERIAL_PORT     0x3F8
13 #define GDB_SERIAL_PORT 0x2F8
14
15 // === IMPRORTS ===
16 #if TRACE_LOCKS
17 extern struct sShortSpinlock    glDebug_Lock;
18 #endif
19 extern int      GetCPUNum(void);
20
21 // === PROTOTYPES ==
22 Uint64  __udivdi3(Uint64 Num, Uint64 Den);
23 Uint64  __umoddi3(Uint64 Num, Uint64 Den);
24
25 // === GLOBALS ===
26  int    gbDebug_SerialSetup = 0;
27  int    gbGDB_SerialSetup = 0;
28
29 // === CODE ===
30 /**
31  * \brief Determine if a short spinlock is locked
32  * \param Lock  Lock pointer
33  */
34 int IS_LOCKED(struct sShortSpinlock *Lock)
35 {
36         return !!Lock->Lock;
37 }
38
39 /**
40  * \brief Check if the current CPU has the lock
41  * \param Lock  Lock pointer
42  */
43 int CPU_HAS_LOCK(struct sShortSpinlock *Lock)
44 {
45         #if STACKED_LOCKS == 1
46         return Lock->Lock == GetCPUNum() + 1;
47         #elif STACKED_LOCKS == 2
48         return Lock->Lock == Proc_GetCurThread();
49         #else
50         return 0;
51         #endif
52 }
53
54 /**
55  * \brief Acquire a Short Spinlock
56  * \param Lock  Lock pointer
57  * 
58  * This type of mutex should only be used for very short sections of code,
59  * or in places where a Mutex_* would be overkill, such as appending
60  * an element to linked list (usually two assignement lines in C)
61  * 
62  * \note This type of lock halts interrupts, so ensure that no timing
63  * functions are called while it is held. As a matter of fact, spend as
64  * little time as possible with this lock held
65  * \note If \a STACKED_LOCKS is set, this type of spinlock can be nested
66  */
67 void SHORTLOCK(struct sShortSpinlock *Lock)
68 {
69          int    v = 1;
70         #if LOCK_DISABLE_INTS
71          int    IF;
72         #endif
73         #if STACKED_LOCKS == 1
74          int    cpu = GetCPUNum() + 1;
75         #elif STACKED_LOCKS == 2
76         void    *thread = Proc_GetCurThread();
77         #endif
78         
79         #if LOCK_DISABLE_INTS
80         // Save interrupt state
81         __ASM__ ("pushf;\n\tpop %0" : "=r"(IF));
82         IF &= 0x200;    // AND out all but the interrupt flag
83         #endif
84         
85         #if STACKED_LOCKS == 1
86         if( Lock->Lock == cpu ) {
87                 Lock->Depth ++;
88                 return ;
89         }
90         #elif STACKED_LOCKS == 2
91         if( Lock->Lock == thread ) {
92                 Lock->Depth ++;
93                 return ;
94         }
95         #endif
96         
97         // Wait for another CPU to release
98         while(v) {
99                 // CMPXCHG:
100                 //  If r/m32 == EAX, set ZF and set r/m32 = r32
101                 //  Else, clear ZF and set EAX = r/m32
102                 #if STACKED_LOCKS == 1
103                 __ASM__("lock cmpxchgl %2, (%3)"
104                         : "=a"(v)
105                         : "a"(0), "r"(cpu), "r"(&Lock->Lock)
106                         );
107                 #elif STACKED_LOCKS == 2
108                 __ASM__("lock cmpxchgl %2, (%3)"
109                         : "=a"(v)
110                         : "a"(0), "r"(thread), "r"(&Lock->Lock)
111                         );
112                 #else
113                 __ASM__("xchgl %%eax, (%%edi)":"=a"(v):"a"(1),"D"(&Lock->Lock));
114                 #endif
115                 
116                 #if LOCK_DISABLE_INTS
117                 if( v ) __ASM__("sti"); // Re-enable interrupts
118                 #endif
119         }
120         
121         #if LOCK_DISABLE_INTS
122         __ASM__("cli");
123         Lock->IF = IF;
124         #endif
125         
126         #if TRACE_LOCKS
127         if( Lock != &glDebug_Lock )
128         {
129                 //Log_Log("LOCK", "%p locked by %p", Lock, __builtin_return_address(0));
130                 LogF("Lock %p locked by %p\n", Lock, __builtin_return_address(0));
131         }
132         #endif
133 }
134 /**
135  * \brief Release a short lock
136  * \param Lock  Lock pointer
137  */
138 void SHORTREL(struct sShortSpinlock *Lock)
139 {       
140         #if STACKED_LOCKS
141         if( Lock->Depth ) {
142                 Lock->Depth --;
143                 return ;
144         }
145         #endif
146         
147         #if TRACE_LOCKS
148         if( Lock != &glDebug_Lock )
149         {
150                 //Log_Log("LOCK", "%p released by %p", Lock, __builtin_return_address(0));
151                 LogF("Lock %p released by %p\n", Lock, __builtin_return_address(0));
152         }
153         #endif
154         
155         #if LOCK_DISABLE_INTS
156         // Lock->IF can change anytime once Lock->Lock is zeroed
157         if(Lock->IF) {
158                 Lock->Lock = 0;
159                 __ASM__ ("sti");
160         }
161         else {
162                 Lock->Lock = 0;
163         }
164         #else
165         Lock->Lock = 0;
166         #endif
167 }
168
169 // === DEBUG IO ===
170 #if USE_GDB_STUB
171 int putDebugChar(char ch)
172 {
173         if(!gbGDB_SerialSetup) {
174                 outb(GDB_SERIAL_PORT + 1, 0x00);    // Disable all interrupts
175                 outb(GDB_SERIAL_PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
176                 outb(GDB_SERIAL_PORT + 0, 0x0C);    // Set divisor to 12 (lo byte) 9600 baud
177                 outb(GDB_SERIAL_PORT + 1, 0x00);    //  (base is         (hi byte)
178                 outb(GDB_SERIAL_PORT + 3, 0x03);    // 8 bits, no parity, one stop bit (8N1)
179                 outb(GDB_SERIAL_PORT + 2, 0xC7);    // Enable FIFO with 14-byte threshold and clear it
180                 outb(GDB_SERIAL_PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
181                 gbDebug_SerialSetup = 1;
182         }
183         while( (inb(GDB_SERIAL_PORT + 5) & 0x20) == 0 );
184         outb(GDB_SERIAL_PORT, ch);
185         return 0;
186 }
187 int getDebugChar(void)
188 {
189         if(!gbGDB_SerialSetup) {
190                 outb(GDB_SERIAL_PORT + 1, 0x00);    // Disable all interrupts
191                 outb(GDB_SERIAL_PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
192                 outb(GDB_SERIAL_PORT + 0, 0x0C);    // Set divisor to 12 (lo byte) 9600 baud
193                 outb(GDB_SERIAL_PORT + 1, 0x00);    //                   (hi byte)
194                 outb(GDB_SERIAL_PORT + 3, 0x03);    // 8 bits, no parity, one stop bit
195                 outb(GDB_SERIAL_PORT + 2, 0xC7);    // Enable FIFO with 14-byte threshold and clear it
196                 outb(GDB_SERIAL_PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
197                 gbDebug_SerialSetup = 1;
198         }
199         while( (inb(GDB_SERIAL_PORT + 5) & 1) == 0)     ;
200         return inb(GDB_SERIAL_PORT);
201 }
202 #endif  /* USE_GDB_STUB */
203
204 void Debug_PutCharDebug(char ch)
205 {
206         #if DEBUG_TO_E9
207         __asm__ __volatile__ ( "outb %%al, $0xe9" :: "a"(((Uint8)ch)) );
208         #endif
209         
210         #if DEBUG_TO_SERIAL
211         if(!gbDebug_SerialSetup) {
212                 outb(SERIAL_PORT + 1, 0x00);    // Disable all interrupts
213                 outb(SERIAL_PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
214                 outb(SERIAL_PORT + 0, 0x0C);    // Set divisor to 12 (lo byte) 9600 baud
215                 outb(SERIAL_PORT + 1, 0x00);    //                   (hi byte)
216                 outb(SERIAL_PORT + 3, 0x03);    // 8 bits, no parity, one stop bit
217                 outb(SERIAL_PORT + 2, 0xC7);    // Enable FIFO with 14-byte threshold and clear it
218                 outb(SERIAL_PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
219                 gbDebug_SerialSetup = 1;
220         }
221         while( (inb(SERIAL_PORT + 5) & 0x20) == 0 );
222         outb(SERIAL_PORT, ch);
223         #endif
224 }
225
226 void Debug_PutStringDebug(const char *String)
227 {
228         while(*String)
229                 Debug_PutCharDebug(*String++);
230 }
231
232 // === IO Commands ===
233 void outb(Uint16 Port, Uint8 Data)
234 {
235         __asm__ __volatile__ ("outb %%al, %%dx"::"d"(Port),"a"(Data));
236 }
237 void outw(Uint16 Port, Uint16 Data)
238 {
239         __asm__ __volatile__ ("outw %%ax, %%dx"::"d"(Port),"a"(Data));
240 }
241 void outd(Uint16 Port, Uint32 Data)
242 {
243         __asm__ __volatile__ ("outl %%eax, %%dx"::"d"(Port),"a"(Data));
244 }
245 Uint8 inb(Uint16 Port)
246 {
247         Uint8   ret;
248         __asm__ __volatile__ ("inb %%dx, %%al":"=a"(ret):"d"(Port));
249         return ret;
250 }
251 Uint16 inw(Uint16 Port)
252 {
253         Uint16  ret;
254         __asm__ __volatile__ ("inw %%dx, %%ax":"=a"(ret):"d"(Port));
255         return ret;
256 }
257 Uint32 ind(Uint16 Port)
258 {
259         Uint32  ret;
260         __asm__ __volatile__ ("inl %%dx, %%eax":"=a"(ret):"d"(Port));
261         return ret;
262 }
263
264 /**
265  * \fn void *memset(void *Dest, int Val, size_t Num)
266  * \brief Do a byte granuality set of Dest
267  */
268 void *memset(void *Dest, int Val, size_t Num)
269 {
270         Uint32  val = Val&0xFF;
271         val |= val << 8;
272         val |= val << 16;
273         __asm__ __volatile__ (
274                 "rep stosl;\n\t"
275                 "mov %3, %%ecx;\n\t"
276                 "rep stosb"
277                 :: "D" (Dest), "a" (val), "c" (Num/4), "r" (Num&3));
278         return Dest;
279 }
280 /**
281  * \brief Set double words
282  */
283 void *memsetd(void *Dest, Uint32 Val, size_t Num)
284 {
285         __asm__ __volatile__ ("rep stosl" :: "D" (Dest), "a" (Val), "c" (Num));
286         return Dest;
287 }
288
289 /**
290  * \fn int memcmp(const void *m1, const void *m2, size_t Num)
291  * \brief Compare two pieces of memory
292  */
293 int memcmp(const void *m1, const void *m2, size_t Num)
294 {
295         const Uint8     *d1 = m1;
296         const Uint8     *d2 = m2;
297         if( Num == 0 )  return 0;       // No bytes are always identical
298         
299         while(Num--)
300         {
301                 if(*d1 != *d2)
302                         return *d1 - *d2;
303                 d1 ++;
304                 d2 ++;
305         }
306         return 0;
307 }
308
309 /**
310  * \fn void *memcpy(void *Dest, const void *Src, size_t Num)
311  * \brief Copy \a Num bytes from \a Src to \a Dest
312  */
313 void *memcpy(void *Dest, const void *Src, size_t Num)
314 {
315         if( ((Uint)Dest & 3) || ((Uint)Src & 3) )
316                 __asm__ __volatile__ ("rep movsb" :: "D" (Dest), "S" (Src), "c" (Num));
317         else {
318                 __asm__ __volatile__ (
319                         "rep movsl;\n\t"
320                         "mov %3, %%ecx;\n\t"
321                         "rep movsb"
322                         :: "D" (Dest), "S" (Src), "c" (Num/4), "r" (Num&3));
323         }
324         return Dest;
325 }
326 /**
327  * \fn void *memcpyd(void *Dest, const void *Src, size_t Num)
328  * \brief Copy \a Num DWORDs from \a Src to \a Dest
329  */
330 void *memcpyd(void *Dest, const void *Src, size_t Num)
331 {
332         __asm__ __volatile__ ("rep movsl" :: "D" (Dest), "S" (Src), "c" (Num));
333         return Dest;
334 }
335
336 /**
337  * \fn Uint64 __udivdi3(Uint64 Num, Uint64 Den)
338  * \brief Divide two 64-bit integers
339  */
340 Uint64 __udivdi3(Uint64 Num, Uint64 Den)
341 {
342         Uint64  P[2];
343         Uint64  q = 0;
344          int    i;
345         
346         if(Den == 0)    __asm__ __volatile__ ("int $0x0");
347         // Common speedups
348         if(Num <= 0xFFFFFFFF && Den <= 0xFFFFFFFF)
349                 return (Uint32)Num / (Uint32)Den;
350         if(Den == 1)    return Num;
351         if(Den == 2)    return Num >> 1;        // Speed Hacks
352         if(Den == 4)    return Num >> 2;        // Speed Hacks
353         if(Den == 8)    return Num >> 3;        // Speed Hacks
354         if(Den == 16)   return Num >> 4;        // Speed Hacks
355         if(Den == 32)   return Num >> 5;        // Speed Hacks
356         if(Den == 1024) return Num >> 10;       // Speed Hacks
357         if(Den == 2048) return Num >> 11;       // Speed Hacks
358         if(Den == 4096) return Num >> 12;
359         if(Num < Den)   return 0;
360         if(Num < Den*2) return 1;
361         if(Num == Den*2)        return 2;
362         
363         #if 1
364         i = 0;  // Shut up
365         P[0] = Num;
366         P[1] = Den;
367         __asm__ __volatile__ (
368                 "fildq %2\n\t"  // Num
369                 "fildq %1\n\t"  // Den
370                 "fdivp\n\t"
371                 "fistpq %0"
372                 : "=m" (q)
373                 : "m" (P[0]), "m" (P[1])
374                 );
375                 
376         //Log("%llx / %llx = %llx\n", Num, Den, q);
377         #else
378         // Restoring division, from wikipedia
379         // http://en.wikipedia.org/wiki/Division_(digital)
380         P[0] = Num;     P[1] = 0;
381         for( i = 64; i--; )
382         {
383                 // P <<= 1;
384                 P[1] = (P[1] << 1) | (P[0] >> 63);
385                 P[0] = P[0] << 1;
386                 
387                 // P -= Den << 64
388                 P[1] -= Den;
389                 
390                 // P >= 0
391                 if( !(P[1] & (1ULL<<63)) ) {
392                         q |= (Uint64)1 << (63-i);
393                 }
394                 else {
395                         //q |= 0 << (63-i);
396                         P[1] += Den;
397                 }
398         }
399         #endif
400         
401         return q;
402 }
403
404 /**
405  * \fn Uint64 __umoddi3(Uint64 Num, Uint64 Den)
406  * \brief Get the modulus of two 64-bit integers
407  */
408 Uint64 __umoddi3(Uint64 Num, Uint64 Den)
409 {
410         if(Den == 0)    __asm__ __volatile__ ("int $0x0");      // Call Div by Zero Error
411         if(Den == 1)    return 0;       // Speed Hacks
412         if(Den == 2)    return Num & 1; // Speed Hacks
413         if(Den == 4)    return Num & 3; // Speed Hacks
414         if(Den == 8)    return Num & 7; // Speed Hacks
415         if(Den == 16)   return Num & 15;        // Speed Hacks
416         if(Den == 32)   return Num & 31;        // Speed Hacks
417         if(Den == 1024) return Num & 1023;      // Speed Hacks
418         if(Den == 2048) return Num & 2047;      // Speed Hacks
419         if(Den == 4096) return Num & 4095;      // Speed Hacks
420         
421         if(Num >> 32 == 0 && Den >> 32 == 0)
422                 return (Uint32)Num % (Uint32)Den;
423         
424         return Num - __udivdi3(Num, Den) * Den;
425 }
426
427 Uint16 LittleEndian16(Uint16 Val)
428 {
429         return Val;
430 }
431 Uint16 BigEndian16(Uint16 Val)
432 {
433         return ((Val&0xFF)<<8) | ((Val>>8)&0xFF);
434 }
435 Uint32 LittleEndian32(Uint32 Val)
436 {
437         return Val;
438 }
439 Uint32 BigEndian32(Uint32 Val)
440 {
441         return ((Val&0xFF)<<24) | ((Val&0xFF00)<<8) | ((Val>>8)&0xFF00) | ((Val>>24)&0xFF);
442 }
443
444 // --- EXPORTS ---
445 EXPORT(memcpy); EXPORT(memset);
446 EXPORT(memcmp);
447 //EXPORT(memcpyw);      EXPORT(memsetw);
448 EXPORT(memcpyd);        EXPORT(memsetd);
449 EXPORT(inb);    EXPORT(inw);    EXPORT(ind);
450 EXPORT(outb);   EXPORT(outw);   EXPORT(outd);
451 EXPORT(__udivdi3);      EXPORT(__umoddi3);
452
453 EXPORT(LittleEndian16); EXPORT(BigEndian16);
454 EXPORT(LittleEndian32); EXPORT(BigEndian32);
455
456 EXPORT(SHORTLOCK);
457 EXPORT(SHORTREL);
458 EXPORT(IS_LOCKED);

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