Merge branch 'master' of git://ted.mutabah.net/acess2
[tpg/acess2.git] / KernelLand / Kernel / arch / x86 / proc.c
1 /*
2  * Acess2 Kernel (x86)
3  * - By John Hodge (thePowersGang)
4  *
5  * proc.c
6  * - Low level thread management
7  */
8 #define DEBUG   0
9 #include <acess.h>
10 #include <threads.h>
11 #include <proc.h>
12 #include <desctab.h>
13 #include <mm_virt.h>
14 #include <errno.h>
15 #include <hal_proc.h>
16 #include <arch_int.h>
17 #include <proc_int.h>
18 #if USE_MP
19 # include <apic.h>
20 #endif
21
22 // === FLAGS ===
23 #define DEBUG_TRACE_SWITCH      0
24 #define DEBUG_DISABLE_DOUBLEFAULT       1
25 #define DEBUG_VERY_SLOW_PERIOD  0
26 #define DEBUG_NOPREEMPT 1
27 #define DISABLE_PIT     0
28
29 // === CONSTANTS ===
30 // Base is 1193182
31 #define TIMER_BASE      1193182
32 #if DISABLE_PIT
33 # define TIMER_DIVISOR  0xFFFF
34 #elif DEBUG_VERY_SLOW_PERIOD
35 # define TIMER_DIVISOR  1193    //~10Hz switch, with 10 quantum = 1s per thread
36 #else
37 # define TIMER_DIVISOR  11932   //~100Hz
38 #endif
39
40 // === TYPES ===
41
42 // === IMPORTS ===
43 extern tGDT     gGDT[];
44 extern tIDT     gIDT[];
45 extern void     APWait(void);   // 16-bit AP pause code
46 extern void     APStartup(void);        // 16-bit AP startup code
47 extern void     NewTaskHeader(tThread *Thread, void *Fcn, int nArgs, ...);      // Actually takes cdecl args
48 extern Uint     Proc_CloneInt(Uint *ESP, Uint32 *CR3, int bNoUserClone);
49 extern Uint32   gaInitPageDir[1024];    // start.asm
50 extern char     Kernel_Stack_Top[];
51 extern int      giNumCPUs;
52 extern tThread  gThreadZero;
53 extern tProcess gProcessZero;
54 extern void     Isr8(void);     // Double Fault
55 extern void     Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack);
56 extern char     scheduler_return[];     // Return address in SchedulerBase
57 extern char     IRQCommon[];    // Common IRQ handler code
58 extern char     IRQCommon_handled[];    // IRQCommon call return location
59 extern char     GetEIP_Sched_ret[];     // GetEIP call return location
60 extern void     Timer_CallTimers(void);
61
62 // === PROTOTYPES ===
63 //void  ArchThreads_Init(void);
64 #if USE_MP
65 void    MP_StartAP(int CPU);
66 void    MP_SendIPIVector(int CPU, Uint8 Vector);
67 void    MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
68 #endif
69 void    Proc_IdleThread(void *Ptr);
70 //void  Proc_Start(void);
71 //tThread       *Proc_GetCurThread(void);
72 void    Proc_ChangeStack(void);
73 // int  Proc_NewKThread(void (*Fcn)(void*), void *Data);
74 // int  Proc_Clone(Uint *Err, Uint Flags);
75 Uint    Proc_MakeUserStack(void);
76 //void  Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
77 void    Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP) NORETURN;
78 void    Proc_CallUser(Uint32 UserIP, Uint32 UserSP, const void *StackData, size_t StackDataLen);
79 //void  Proc_CallFaultHandler(tThread *Thread);
80 //void  Proc_DumpThreadCPUState(tThread *Thread);
81 void    Proc_Scheduler(int CPU);
82
83 // === GLOBALS ===
84 // --- Multiprocessing ---
85 #if USE_MP
86 volatile int    giNumInitingCPUs = 0;
87 volatile Uint32 giMP_TimerCount;        // Start Count for Local APIC Timer
88 tAPIC   *gpMP_LocalAPIC = NULL;
89 Uint8   gaAPIC_to_CPU[256] = {0};
90  int    giProc_BootProcessorID = 0;
91 tTSS    gaTSSs[MAX_CPUS];       // TSS Array
92 #endif
93 tCPU    gaCPUs[MAX_CPUS] = {
94         {.Current = &gThreadZero}
95         };
96 tTSS    *gTSSs = NULL;  // Pointer to TSS array
97 tTSS    gTSS0 = {0};
98 // --- Error Recovery ---
99 char    gaDoubleFaultStack[1024] __attribute__ ((section(".padata")));
100 tTSS    gDoubleFault_TSS = {
101         .ESP0 = (Uint)&gaDoubleFaultStack[1024],
102         .SS0 = 0x10,
103         .CR3 = (Uint)gaInitPageDir - KERNEL_BASE,
104         .EIP = (Uint)Isr8,
105         .ESP = (Uint)&gaDoubleFaultStack[1024],
106         .CS = 0x08,     .SS = 0x10,
107         .DS = 0x10,     .ES = 0x10,
108         .FS = 0x10,     .GS = 0x10,
109 };
110
111 // === CODE ===
112 /**
113  * \fn void ArchThreads_Init(void)
114  * \brief Starts the process scheduler
115  */
116 void ArchThreads_Init(void)
117 {
118         // Mark BSP as active
119         gaCPUs[0].State = 2;
120         
121         #if USE_MP
122         // -- Initialise Multiprocessing
123         const void *mpfloatptr = MPTable_LocateFloatPtr();
124         if( mpfloatptr )
125         {
126                 giNumCPUs = MPTable_FillCPUs(mpfloatptr, gaCPUs, MAX_CPUS, &giProc_BootProcessorID);
127                 for( int i = 0; i < giNumCPUs; i ++ )
128                 {
129                         // TODO: Determine if there's an overlap
130                         gaAPIC_to_CPU[gaCPUs[i].APICID] = i;
131                 }
132                 gTSSs = gaTSSs;
133         }
134         else
135         {
136                 Log("No MP Table was found, assuming uniprocessor");
137                 giNumCPUs = 1;
138                 gTSSs = &gTSS0;
139         }
140         #else
141         giNumCPUs = 1;
142         gTSSs = &gTSS0;
143         #endif
144         
145         #if !DEBUG_DISABLE_DOUBLEFAULT
146         // Initialise Double Fault TSS
147         gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF;
148         gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16;
149         gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24;
150         
151         // Set double fault IDT to use the new TSS
152         gIDT[8].OffsetLo = 0;
153         gIDT[8].CS = 5<<3;
154         gIDT[8].Flags = 0x8500;
155         gIDT[8].OffsetHi = 0;
156         #endif
157         
158         // Set timer frequency
159         outb(0x43, 0x34);       // Set Channel 0, Low/High, Rate Generator
160         outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
161         outb(0x40, (TIMER_DIVISOR>>8)&0xFF);    // High Byte
162         
163         Log_Debug("Proc", "PIT Frequency %i.%03i Hz",
164                 TIMER_BASE/TIMER_DIVISOR,
165                 ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000
166                 );
167         
168         #if USE_MP
169         // Get the count setting for APIC timer
170         Log("Determining APIC Count");
171         __asm__ __volatile__ ("sti");
172         while( giMP_TimerCount == 0 )   __asm__ __volatile__ ("hlt");
173         __asm__ __volatile__ ("cli");
174         Log("APIC Count %i", giMP_TimerCount);
175         {
176                 Uint64  freq = giMP_TimerCount;
177                 freq *= TIMER_BASE;
178                 freq /= TIMER_DIVISOR;
179                 if( (freq /= 1000) < 2*1000)
180                         Log("Bus Frequency %i KHz", freq);
181                 else if( (freq /= 1000) < 2*1000)
182                         Log("Bus Frequency %i MHz", freq);
183                 else if( (freq /= 1000) < 2*1000)
184                         Log("Bus Frequency %i GHz", freq);
185                 else
186                         Log("Bus Frequency %i THz", freq);
187         }
188         
189         // Initialise Normal TSS(s)
190         for(int pos=0;pos<giNumCPUs;pos++)
191         {
192         #else
193         const int pos = 0;
194         #endif
195                 gTSSs[pos].SS0 = 0x10;
196                 gTSSs[pos].ESP0 = 0;    // Set properly by scheduler
197                 gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
198                 gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos]) >> 16) & 0xFFFF;
199                 gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
200         #if USE_MP
201         }
202         #endif
203         
204         // Load the BSP's TSS
205         __asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
206         // Set Current Thread and CPU Number in DR0 and DR1
207         __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero));
208         __asm__ __volatile__ ("mov %0, %%db1"::"r"(0));
209         
210         gaCPUs[0].Current = &gThreadZero;
211         gThreadZero.CurCPU = 0;
212         
213         gProcessZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE;
214         
215         // Create Per-Process Data Block
216         if( MM_Allocate( (void*)MM_PPD_CFG ) == 0 )
217         {
218                 Panic("OOM - No space for initial Per-Process Config");
219         }
220
221         // Initialise SSE support
222         Proc_InitialiseSSE();
223         
224         // Change Stacks
225         Proc_ChangeStack();
226 }
227
228 #if USE_MP
229 /**
230  * \brief Start an AP
231  */
232 void MP_StartAP(int CPU)
233 {
234         Log_Log("Proc", "Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
235         
236         // Set location of AP startup code and mark for a warm restart
237         *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
238         *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
239         outb(0x70, 0x0F);       outb(0x71, 0x0A);       // Set warm reset flag
240         MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);   // Init IPI
241
242         // Take a quick nap (20ms)
243         Time_Delay(20);
244
245         // TODO: Use a better address, preferably registered with the MM
246         // - MM_AllocDMA mabye?
247         // Create a far jump
248         *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA;  // Far JMP
249         *(Uint16*)(KERNEL_BASE|0x11001) = (Uint16)(tVAddr)&APStartup + 0x10;    // IP
250         *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF;       // CS
251         
252         giNumInitingCPUs ++;
253         
254         // Send a Startup-IPI to make the CPU execute at 0x11000 (which we
255         // just filled)
256         MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6);        // StartupIPI
257         
258         tTime   timeout = now() + 2;
259         while( giNumInitingCPUs && now() > timeout )
260                 HALT();
261         
262         if( giNumInitingCPUs == 0 )
263                 return ;
264         
265         // First S-IPI failed, send again
266         MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6);
267         timeout = now() + 2;
268         while( giNumInitingCPUs && now() > timeout )
269                 HALT();
270         if( giNumInitingCPUs == 0 )
271                 return ;
272
273         Log_Notice("Proc", "CPU %i (APIC %x) didn't come up", CPU, gaCPUs[CPU].APICID); 
274
275         // Oh dammit.
276         giNumInitingCPUs = 0;
277 }
278
279 void MP_SendIPIVector(int CPU, Uint8 Vector)
280 {
281         MP_SendIPI(gaCPUs[CPU].APICID, Vector, 0);
282 }
283
284 /**
285  * \brief Send an Inter-Processor Interrupt
286  * \param APICID        Processor's Local APIC ID
287  * \param Vector        Argument of some kind
288  * \param DeliveryMode  Type of signal
289  * \note 3A 10.5 "APIC/Handling Local Interrupts"
290  */
291 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
292 {
293         Uint32  val;
294         
295         // Hi
296         val = (Uint)APICID << 24;
297         gpMP_LocalAPIC->ICR[1].Val = val;
298         // Low (and send)
299         val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
300         gpMP_LocalAPIC->ICR[0].Val = val;
301 }
302 #endif
303
304 void Proc_IdleThread(void *Ptr)
305 {
306         tCPU    *cpu = &gaCPUs[GetCPUNum()];
307         cpu->Current->ThreadName = strdup("Idle Thread");
308         Threads_SetPriority( cpu->Current, -1 );        // Never called randomly
309         cpu->Current->Quantum = 1;      // 1 slice quantum
310         for(;;) {
311                 __asm__ __volatile__ ("sti");   // Make sure interrupts are enabled
312                 __asm__ __volatile__ ("hlt");
313                 Proc_Reschedule();
314         }
315 }
316
317 /**
318  * \fn void Proc_Start(void)
319  * \brief Start process scheduler
320  */
321 void Proc_Start(void)
322 {
323         #if USE_MP
324         // BSP still should run the current task
325         gaCPUs[giProc_BootProcessorID].Current = &gThreadZero;
326         
327         __asm__ __volatile__ ("sti");
328         
329         // Start APs
330         for( int i = 0; i < giNumCPUs; i ++ )
331         {
332                 if(i != giProc_BootProcessorID)
333                         gaCPUs[i].Current = NULL;
334
335                 // Create Idle Task
336                 Proc_NewKThread(Proc_IdleThread, &gaCPUs[i]);
337                 
338                 // Start the AP
339                 if( i != giProc_BootProcessorID ) {
340                         MP_StartAP( i );
341                 }
342         }
343         #else
344         // Create Idle Task
345         Proc_NewKThread(Proc_IdleThread, &gaCPUs[0]);
346         
347         // Set current task
348         gaCPUs[0].Current = &gThreadZero;
349
350         // Start Interrupts (and hence scheduler)
351         __asm__ __volatile__("sti");
352         #endif
353         MM_FinishVirtualInit();
354 }
355
356 /**
357  * \fn tThread *Proc_GetCurThread(void)
358  * \brief Gets the current thread
359  */
360 tThread *Proc_GetCurThread(void)
361 {
362         #if USE_MP
363         return gaCPUs[ GetCPUNum() ].Current;
364         #else
365         return gaCPUs[ 0 ].Current;
366         #endif
367 }
368
369 /**
370  * \fn void Proc_ChangeStack(void)
371  * \brief Swaps the current stack for a new one (in the proper stack reigon)
372  */
373 void Proc_ChangeStack(void)
374 {
375         Uint    esp, ebp;
376         Uint    tmpEbp, oldEsp;
377         Uint    curBase, newBase;
378
379         __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
380         __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
381
382         oldEsp = esp;
383
384         // Create new KStack
385         newBase = MM_NewKStack();
386         // Check for errors
387         if(newBase == 0) {
388                 Panic("What the?? Unable to allocate space for initial kernel stack");
389                 return;
390         }
391
392         curBase = (Uint)&Kernel_Stack_Top;
393         
394         LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
395
396         // Get ESP as a used size
397         esp = curBase - esp;
398         LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp );
399         // Copy used stack
400         memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp );
401         // Get ESP as an offset in the new stack
402         esp = newBase - esp;
403         // Adjust EBP
404         ebp = newBase - (curBase - ebp);
405
406         // Repair EBPs & Stack Addresses
407         // Catches arguments also, but may trash stack-address-like values
408         for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4)
409         {
410                 if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase)
411                         *(Uint*)tmpEbp += newBase - curBase;
412         }
413         
414         Proc_GetCurThread()->KernelStack = newBase;
415         
416         __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp));
417         __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp));
418 }
419
420 void Proc_ClearProcess(tProcess *Process)
421 {
422         MM_ClearSpace(Process->MemState.CR3);
423 }
424
425 void Proc_ClearThread(tThread *Thread)
426 {
427         if(Thread->SavedState.SSE) {
428                 free(Thread->SavedState.SSE);
429                 Thread->SavedState.SSE = NULL;
430         }
431 }
432
433 tTID Proc_NewKThread(void (*Fcn)(void*), void *Data)
434 {
435         Uint    esp;
436         tThread *newThread;
437         
438         newThread = Threads_CloneTCB(0);
439         if(!newThread)  return -1;
440         
441         // Create new KStack
442         newThread->KernelStack = MM_NewKStack();
443         // Check for errors
444         if(newThread->KernelStack == 0) {
445                 free(newThread);
446                 return -1;
447         }
448
449         esp = newThread->KernelStack;
450         *(Uint*)(esp-=4) = (Uint)Data;  // Data (shadowed)
451         *(Uint*)(esp-=4) = 1;   // Number of params
452         *(Uint*)(esp-=4) = (Uint)Fcn;   // Function to call
453         *(Uint*)(esp-=4) = (Uint)newThread;     // Thread ID
454         
455         newThread->SavedState.ESP = esp;
456         newThread->SavedState.EIP = (Uint)&NewTaskHeader;
457         newThread->SavedState.SSE = NULL;
458 //      Log("New (KThread) %p, esp = %p", newThread->SavedState.EIP, newThread->SavedState.ESP);
459         
460 //      MAGIC_BREAK();  
461         Threads_AddActive(newThread);
462
463         return newThread->TID;
464 }
465
466 /**
467  * \fn int Proc_Clone(Uint *Err, Uint Flags)
468  * \brief Clone the current process
469  */
470 tPID Proc_Clone(Uint Flags)
471 {
472         tThread *newThread;
473         tThread *cur = Proc_GetCurThread();
474         Uint    eip;
475
476         // Sanity, please
477         if( !(Flags & CLONE_VM) ) {
478                 Log_Error("Proc", "Proc_Clone: Don't leave CLONE_VM unset, use Proc_NewKThread instead");
479                 return -1;
480         }
481         
482         // New thread
483         newThread = Threads_CloneTCB(Flags);
484         if(!newThread)  return -1;
485
486         newThread->KernelStack = cur->KernelStack;
487
488         // Clone state
489         eip = Proc_CloneInt(&newThread->SavedState.ESP, &newThread->Process->MemState.CR3, Flags & CLONE_NOUSER);
490         if( eip == 0 ) {
491                 return 0;
492         }
493         newThread->SavedState.EIP = eip;
494         newThread->SavedState.SSE = NULL;
495         newThread->SavedState.bSSEModified = 0;
496         
497         // Check for errors
498         if( newThread->Process->MemState.CR3 == 0 ) {
499                 Log_Error("Proc", "Proc_Clone: MM_Clone failed");
500                 Threads_Delete(newThread);
501                 return -1;
502         }
503
504         // Add the new thread to the run queue
505         Threads_AddActive(newThread);
506         return newThread->TID;
507 }
508
509 /**
510  * \fn int Proc_SpawnWorker(void)
511  * \brief Spawns a new worker thread
512  */
513 tThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
514 {
515         Uint    stack_contents[4];
516         LOG("(Fcn=%p,Data=%p)", Fcn, Data);
517         
518         // Create new thread
519         tThread *new = Threads_CloneThreadZero();
520         LOG("new=%p", new);
521         if(!new) {
522                 Warning("Proc_SpawnWorker - Out of heap space!\n");
523                 return NULL;
524         }
525
526         // Create the stack contents
527         stack_contents[3] = (Uint)Data;
528         stack_contents[2] = 1;
529         stack_contents[1] = (Uint)Fcn;
530         stack_contents[0] = (Uint)new;
531         
532         // Create a new worker stack (in PID0's address space)
533         new->KernelStack = MM_NewWorkerStack(stack_contents, sizeof(stack_contents));
534         LOG("new->KernelStack = %p", new->KernelStack);
535
536         // Save core machine state
537         new->SavedState.ESP = new->KernelStack - sizeof(stack_contents);
538         new->SavedState.EIP = (Uint)NewTaskHeader;
539         new->SavedState.SSE = NULL;
540         new->SavedState.bSSEModified = 0;
541         
542         // Mark as active
543         new->Status = THREAD_STAT_PREINIT;
544         Threads_AddActive( new );
545         LOG("Added to active");
546         
547         return new;
548 }
549
550 /**
551  * \fn Uint Proc_MakeUserStack(void)
552  * \brief Creates a new user stack
553  */
554 Uint Proc_MakeUserStack(void)
555 {
556         tPage   *base = (void*)(USER_STACK_TOP - USER_STACK_SZ);
557         
558         // Check Prospective Space
559         for( Uint i = USER_STACK_SZ/PAGE_SIZE; i--; )
560         {
561                 if( MM_GetPhysAddr( base + i ) != 0 )
562                 {
563                         Warning("Proc_MakeUserStack: Address %p in use", base + i);
564                         return 0;
565                 }
566         }
567         // Allocate Stack - Allocate incrementally to clean up MM_Dump output
568         for( Uint i = 0; i < USER_STACK_SZ/PAGE_SIZE; i++ )
569         {
570                 if( MM_Allocate( base + i ) == 0 )
571                 {
572                         Warning("OOM: Proc_MakeUserStack");
573                         return 0;
574                 }
575         }
576         
577         return (tVAddr)( base + USER_STACK_SZ/PAGE_SIZE );
578 }
579
580 void Proc_StartUser(Uint Entrypoint, Uint Base, int ArgC, const char **ArgV, int DataSize)
581 {
582         Uint    *stack;
583          int    i;
584         const char      **envp = NULL;
585         Uint16  ss, cs;
586         
587         // Copy data to the user stack and free original buffer
588         stack = (void*)Proc_MakeUserStack();
589         stack -= (DataSize+sizeof(*stack)-1)/sizeof(*stack);
590         memcpy( stack, ArgV, DataSize );
591         free(ArgV);
592         
593         // Adjust Arguments and environment
594         if( DataSize )
595         {
596                 Uint delta = (Uint)stack - (Uint)ArgV;
597                 ArgV = (const char**)stack;
598                 for( i = 0; ArgV[i]; i++ )      ArgV[i] += delta;
599                 envp = &ArgV[i+1];
600                 for( i = 0; envp[i]; i++ )      envp[i] += delta;
601         }
602         
603         // User Mode Segments
604         ss = 0x23;      cs = 0x1B;
605         
606         // Arguments
607         *--stack = (Uint)envp;
608         *--stack = (Uint)ArgV;
609         *--stack = (Uint)ArgC;
610         *--stack = Base;
611         
612         Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
613 }
614
615 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
616 {
617         Uint    *stack = (void*)Stack;
618         *--stack = SS;          //Stack Segment
619         *--stack = Stack;       //Stack Pointer
620         *--stack = Flags;       //EFLAGS (Resvd (0x2) and IF (0x20))
621         *--stack = CS;          //Code Segment
622         *--stack = IP;  //EIP
623         //PUSHAD
624         *--stack = 0xAAAAAAAA;  // eax
625         *--stack = 0xCCCCCCCC;  // ecx
626         *--stack = 0xDDDDDDDD;  // edx
627         *--stack = 0xBBBBBBBB;  // ebx
628         *--stack = 0xD1D1D1D1;  // edi
629         *--stack = 0x54545454;  // esp - NOT POPED
630         *--stack = 0x51515151;  // esi
631         *--stack = 0xB4B4B4B4;  // ebp
632         //Individual PUSHs
633         *--stack = SS;  // ds
634         *--stack = SS;  // es
635         *--stack = SS;  // fs
636         *--stack = SS;  // gs
637         
638         __asm__ __volatile__ (
639         "mov %%eax,%%esp;\n\t"  // Set stack pointer
640         "pop %%gs;\n\t"
641         "pop %%fs;\n\t"
642         "pop %%es;\n\t"
643         "pop %%ds;\n\t"
644         "popa;\n\t"
645         "iret;\n\t" : : "a" (stack));
646         for(;;);
647 }
648
649 void Proc_CallUser(Uint32 UserIP, Uint32 UserSP, const void *StackData, size_t StackDataLen)
650 {
651         if( UserSP < StackDataLen )
652                 return ;
653         if( !CheckMem( (void*)(UserSP - StackDataLen), StackDataLen ) )
654                 return ;
655         memcpy( (void*)(UserSP - StackDataLen), StackData, StackDataLen );
656         
657         __asm__ __volatile__ (
658                 "mov $0x23,%%ax;\n\t"
659                 "mov %%ax, %%ds;\n\t"
660                 "mov %%ax, %%es;\n\t"
661                 "mov %%ax, %%fs;\n\t"
662                 "mov %%ax, %%gs;\n\t"
663                 "push $0x23;\n\t"
664                 "push %1;\n\t"
665                 "push $0x202;\n\t"
666                 "push $0x1B;\n\t"
667                 "push %0;\n\t"
668                 "iret;\n\t"
669                 :
670                 : "r" (UserIP), "r" (UserSP - StackDataLen)
671                 : "eax"
672                 );
673         for(;;)
674                 ;
675 }
676
677 /**
678  * \brief Calls a signal handler in user mode
679  * \note Used for signals
680  */
681 void Proc_CallFaultHandler(tThread *Thread)
682 {
683         // Rewinds the stack and calls the user function
684         // Never returns
685         Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack );
686         for(;;);
687 }
688
689 void Proc_DumpThreadCPUState(tThread *Thread)
690 {
691         if( Thread->CurCPU > -1 )
692         {
693                  int    maxBacktraceDistance = 6;
694                 tRegs   *regs = NULL;
695                 Uint32  *stack;
696                 
697                 if( Thread->CurCPU != GetCPUNum() ) {
698                         Log("  Currently running");
699                         return ;
700                 }
701                 
702                 // Backtrace to find the IRQ entrypoint
703                 // - This will usually only be called by an IRQ, so this should
704                 //   work
705                 __asm__ __volatile__ ("mov %%ebp, %0" : "=r" (stack));
706                 while( maxBacktraceDistance -- )
707                 {
708                         if( !CheckMem(stack, 8) ) {
709                                 regs = NULL;
710                                 break;
711                         }
712                         // [ebp] = oldEbp
713                         // [ebp+4] = retaddr
714                         
715                         if( stack[1] == (tVAddr)&IRQCommon_handled ) {
716                                 regs = (void*)stack[2];
717                                 break;
718                         }
719                         
720                         stack = (void*)stack[0];
721                 }
722                 
723                 if( !regs ) {
724                         Log("  Unable to find IRQ Entry");
725                         return ;
726                 }
727                 
728                 Log("  at %04x:%08x [EAX:%x]", regs->cs, regs->eip, regs->eax);
729                 Error_Backtrace(regs->eip, regs->ebp);
730                 return ;
731         }
732
733         Log(" Saved = %p (SP=%p)", Thread->SavedState.EIP, Thread->SavedState.ESP);     
734
735         tVAddr  diffFromScheduler = Thread->SavedState.EIP - (tVAddr)SwitchTasks;
736         tVAddr  diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_CloneInt;
737         tVAddr  diffFromSpawn = Thread->SavedState.EIP - (tVAddr)NewTaskHeader;
738         
739         if( diffFromClone > 0 && diffFromClone < 40 )   // When I last checked, .newTask was at .+27
740         {
741                 Log("  Creating process");
742                 return ;
743         }
744         
745         if( diffFromSpawn == 0 )
746         {
747                 Log("  Creating thread");
748                 return ;
749         }
750         
751         if( diffFromScheduler > 0 && diffFromScheduler < 128 )  // When I last checked, GetEIP was at .+0x30
752         {
753                 // Scheduled out
754                 Log("  At %04x:%08x", Thread->SavedState.UserCS, Thread->SavedState.UserEIP);
755                 return ;
756         }
757         
758         Log("  Just created (unknown %p)", Thread->SavedState.EIP);
759 }
760
761 void Proc_Reschedule(void)
762 {
763         tThread *nextthread, *curthread;
764          int    cpu = GetCPUNum();
765
766         // TODO: Wait for the lock?
767         if(IS_LOCKED(&glThreadListLock))        return;
768         
769         curthread = Proc_GetCurThread();
770
771         nextthread = Threads_GetNextToRun(cpu, curthread);
772
773         if(!nextthread || nextthread == curthread)
774                 return ;
775
776         #if DEBUG_TRACE_SWITCH
777         // HACK: Ignores switches to the idle threads
778         if( nextthread->TID == 0 || nextthread->TID > giNumCPUs )
779         {
780                 LogF("\nSwitching CPU %i to %p (%i %s) - CR3 = 0x%x, EIP = %p, ESP = %p\n",
781                         GetCPUNum(),
782                         nextthread, nextthread->TID, nextthread->ThreadName,
783                         nextthread->Process->MemState.CR3,
784                         nextthread->SavedState.EIP,
785                         nextthread->SavedState.ESP
786                         );
787                 LogF("OldCR3 = %P\n", curthread->Process->MemState.CR3);
788         }
789         #endif
790
791         // Update CPU state
792         gaCPUs[cpu].Current = nextthread;
793         gaCPUs[cpu].LastTimerThread = NULL;
794         gTSSs[cpu].ESP0 = nextthread->KernelStack-4;
795         __asm__ __volatile__("mov %0, %%db0\n\t" : : "r"(nextthread) );
796
797         // Save FPU/MMX/XMM/SSE state
798         if( curthread && curthread->SavedState.SSE )
799         {
800                 Proc_SaveSSE( ((Uint)curthread->SavedState.SSE + 0xF) & ~0xF );
801                 curthread->SavedState.bSSEModified = 0;
802                 Proc_DisableSSE();
803         }
804
805         if( curthread )
806         {
807                 SwitchTasks(
808                         nextthread->SavedState.ESP, &curthread->SavedState.ESP,
809                         nextthread->SavedState.EIP, &curthread->SavedState.EIP,
810                         nextthread->Process->MemState.CR3
811                         );
812         }
813         else
814         {
815                 SwitchTasks(
816                         nextthread->SavedState.ESP, 0,
817                         nextthread->SavedState.EIP, 0,
818                         nextthread->Process->MemState.CR3
819                         );
820         }
821
822         return ;
823 }
824
825 /**
826  * \fn void Proc_Scheduler(int CPU)
827  * \brief Swap current thread and clears dead threads
828  */
829 void Proc_Scheduler(int CPU)
830 {
831         #if USE_MP
832         if( GetCPUNum() )
833                 gpMP_LocalAPIC->EOI.Val = 0;
834         else
835         #endif
836                 outb(0x20, 0x20);
837         __asm__ __volatile__ ("sti");   
838
839         // Call the timer update code
840         Timer_CallTimers();
841
842         #if !DEBUG_NOPREEMPT
843         // If two ticks happen within the same task, and it's not an idle task, swap
844         if( gaCPUs[CPU].Current->TID > giNumCPUs && gaCPUs[CPU].Current == gaCPUs[CPU].LastTimerThread )
845         {
846                 Proc_Reschedule();
847         }
848         
849         gaCPUs[CPU].LastTimerThread = gaCPUs[CPU].Current;
850         #endif
851 }
852
853 // === EXPORTS ===
854 EXPORT(Proc_SpawnWorker);

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