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

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