Helps if I test in BOTH builds
[tpg/acess2.git] / Kernel / arch / x86 / proc.c
1 /*
2  * AcessOS Microkernel Version
3  * proc.c
4  */
5 #include <acess.h>
6 #include <threads.h>
7 #include <proc.h>
8 #include <desctab.h>
9 #include <mm_virt.h>
10 #include <errno.h>
11 #if USE_MP
12 # include <mp.h>
13 #endif
14
15 // === FLAGS ===
16 #define DEBUG_TRACE_SWITCH      0
17
18 // === CONSTANTS ===
19 #define SWITCH_MAGIC    0xFFFACE55      // There is no code in this area
20 // Base is 1193182
21 #define TIMER_BASE      1193182
22 #define TIMER_DIVISOR   11931   //~100Hz
23
24 // === TYPES ===
25 #if USE_MP
26 typedef struct sCPU
27 {
28         Uint8   APICID;
29         Uint8   State;  // 0: Unavaliable, 1: Idle, 2: Active
30         Uint16  Resvd;
31         tThread *Current;
32         tThread *IdleThread;
33 }       tCPU;
34 #endif
35
36 // === IMPORTS ===
37 extern tGDT     gGDT[];
38 extern tIDT     gIDT[];
39 extern void APWait(void);       // 16-bit AP pause code
40 extern void APStartup(void);    // 16-bit AP startup code
41 extern Uint     GetEIP(void);   // start.asm
42 extern int      GetCPUNum(void);        // start.asm
43 extern Uint32   gaInitPageDir[1024];    // start.asm
44 extern void     Kernel_Stack_Top;
45 extern tSpinlock        glThreadListLock;
46 extern int      giNumCPUs;
47 extern int      giNextTID;
48 extern int      giTotalTickets;
49 extern int      giNumActiveThreads;
50 extern tThread  gThreadZero;
51 extern tThread  *gActiveThreads;
52 extern tThread  *gSleepingThreads;
53 extern tThread  *gDeleteThreads;
54 extern void     Threads_Dump(void);
55 extern tThread  *Threads_CloneTCB(Uint *Err, Uint Flags);
56 extern void     Isr8(void);     // Double Fault
57 extern void     Proc_ReturnToUser(void);
58
59 // === PROTOTYPES ===
60 void    ArchThreads_Init(void);
61 #if USE_MP
62 void    MP_StartAP(int CPU);
63 void    MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
64 #endif
65 void    Proc_Start(void);
66 tThread *Proc_GetCurThread(void);
67 void    Proc_ChangeStack(void);
68  int    Proc_Clone(Uint *Err, Uint Flags);
69 void    Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP);
70 void    Proc_CallFaultHandler(tThread *Thread);
71 void    Proc_Scheduler(int CPU);
72
73 // === GLOBALS ===
74 // --- Multiprocessing ---
75 #if USE_MP
76 volatile int    giNumInitingCPUs = 0;
77 tMPInfo *gMPFloatPtr = NULL;
78 volatile Uint32 giMP_TimerCount;        // Start Count for Local APIC Timer
79 tAPIC   *gpMP_LocalAPIC = NULL;
80 Uint8   gaAPIC_to_CPU[256] = {0};
81 tCPU    gaCPUs[MAX_CPUS];
82 tTSS    gaTSSs[MAX_CPUS];       // TSS Array
83  int    giProc_BootProcessorID = 0;
84 #else
85 tThread *gCurrentThread = NULL;
86 tThread *gpIdleThread = NULL;
87 #endif
88 #if USE_PAE
89 Uint32  *gPML4s[4] = NULL;
90 #endif
91 tTSS    *gTSSs = NULL;  // Pointer to TSS array
92 tTSS    gTSS0 = {0};
93 // --- Error Recovery ---
94 char    gaDoubleFaultStack[1024] __attribute__ ((section(".padata")));
95 tTSS    gDoubleFault_TSS = {
96         .ESP0 = (Uint)&gaDoubleFaultStack[1024],
97         .SS0 = 0x10,
98         .CR3 = (Uint)gaInitPageDir - KERNEL_BASE,
99         .EIP = (Uint)Isr8,
100         .ESP = (Uint)&gaDoubleFaultStack[1024],
101         .CS = 0x08,     .SS = 0x10,
102         .DS = 0x10,     .ES = 0x10,
103         .FS = 0x10,     .GS = 0x10,
104 };
105
106 // === CODE ===
107 /**
108  * \fn void ArchThreads_Init(void)
109  * \brief Starts the process scheduler
110  */
111 void ArchThreads_Init(void)
112 {
113         Uint    pos = 0;
114         
115         #if USE_MP
116         tMPTable        *mptable;
117         
118         // Mark BSP as active
119         gaCPUs[0].State = 2;
120         
121         // -- Initialise Multiprocessing
122         // Find MP Floating Table
123         // - EBDA/Last 1Kib (640KiB)
124         for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
125                 if( *(Uint*)(pos) == MPPTR_IDENT ) {
126                         Log("Possible %p", pos);
127                         if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
128                         gMPFloatPtr = (void*)pos;
129                         break;
130                 }
131         }
132         // - Last KiB (512KiB base mem)
133         if(!gMPFloatPtr) {
134                 for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
135                         if( *(Uint*)(pos) == MPPTR_IDENT ) {
136                                 Log("Possible %p", pos);
137                                 if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
138                                 gMPFloatPtr = (void*)pos;
139                                 break;
140                         }
141                 }
142         }
143         // - BIOS ROM
144         if(!gMPFloatPtr) {
145                 for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
146                         if( *(Uint*)(pos) == MPPTR_IDENT ) {
147                                 Log("Possible %p", pos);
148                                 if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
149                                 gMPFloatPtr = (void*)pos;
150                                 break;
151                         }
152                 }
153         }
154         
155         // If the MP Table Exists, parse it
156         if(gMPFloatPtr)
157         {
158                  int    i;
159                 tMPTable_Ent    *ents;
160                 Log("gMPFloatPtr = %p", gMPFloatPtr);
161                 Log("*gMPFloatPtr = {");
162                 Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
163                 Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
164                 Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
165                 Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
166                 Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
167                 Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
168                         gMPFloatPtr->Features[0],       gMPFloatPtr->Features[1],
169                         gMPFloatPtr->Features[2],       gMPFloatPtr->Features[3],
170                         gMPFloatPtr->Features[4]
171                         );
172                 Log("}");
173                 
174                 mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
175                 Log("mptable = %p", mptable);
176                 Log("*mptable = {");
177                 Log("\t.Sig = 0x%08x", mptable->Sig);
178                 Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
179                 Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
180                 Log("\t.Checksum = 0x%02x", mptable->Checksum);
181                 Log("\t.OEMID = '%8c'", mptable->OemID);
182                 Log("\t.ProductID = '%8c'", mptable->ProductID);
183                 Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
184                 Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
185                 Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
186                 Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
187                 Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
188                 Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
189                 Log("}");
190                 
191                 gpMP_LocalAPIC = (void*)MM_MapHWPages(mptable->LocalAPICMemMap, 1);
192                 
193                 ents = mptable->Entries;
194                 giNumCPUs = 0;
195                 
196                 for( i = 0; i < mptable->EntryCount; i ++ )
197                 {
198                          int    entSize = 0;
199                         switch( ents->Type )
200                         {
201                         case 0: // Processor
202                                 entSize = 20;
203                                 Log("%i: Processor", i);
204                                 Log("\t.APICID = %i", ents->Proc.APICID);
205                                 Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
206                                 Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
207                                 Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
208                                 Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
209                                 
210                                 
211                                 if( !(ents->Proc.CPUFlags & 1) ) {
212                                         Log("DISABLED");
213                                         break;
214                                 }
215                                 
216                                 // Check if there is too many processors
217                                 if(giNumCPUs >= MAX_CPUS) {
218                                         giNumCPUs ++;   // If `giNumCPUs` > MAX_CPUS later, it will be clipped
219                                         break;
220                                 }
221                                 
222                                 // Initialise CPU Info
223                                 gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
224                                 gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
225                                 gaCPUs[giNumCPUs].State = 0;
226                                 giNumCPUs ++;
227                                 
228                                 // Set BSP Variable
229                                 if( ents->Proc.CPUFlags & 2 ) {
230                                         giProc_BootProcessorID = giNumCPUs-1;
231                                 }
232                                 
233                                 break;
234                         
235                         #if DUMP_MP_TABLES
236                         case 1: // Bus
237                                 entSize = 8;
238                                 Log("%i: Bus", i);
239                                 Log("\t.ID = %i", ents->Bus.ID);
240                                 Log("\t.TypeString = '%6C'", ents->Bus.TypeString);
241                                 break;
242                         case 2: // I/O APIC
243                                 entSize = 8;
244                                 Log("%i: I/O APIC", i);
245                                 Log("\t.ID = %i", ents->IOAPIC.ID);
246                                 Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
247                                 Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
248                                 Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
249                                 break;
250                         case 3: // I/O Interrupt Assignment
251                                 entSize = 8;
252                                 Log("%i: I/O Interrupt Assignment", i);
253                                 Log("\t.IntType = %i", ents->IOInt.IntType);
254                                 Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
255                                 Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
256                                 Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
257                                 Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
258                                 Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
259                                 break;
260                         case 4: // Local Interrupt Assignment
261                                 entSize = 8;
262                                 Log("%i: Local Interrupt Assignment", i);
263                                 Log("\t.IntType = %i", ents->LocalInt.IntType);
264                                 Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
265                                 Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
266                                 Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
267                                 Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
268                                 Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
269                                 break;
270                         default:
271                                 Log("%i: Unknown (%i)", i, ents->Type);
272                                 break;
273                         #endif
274                         }
275                         ents = (void*)( (Uint)ents + entSize );
276                 }
277                 
278                 if( giNumCPUs > MAX_CPUS ) {
279                         Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
280                         giNumCPUs = MAX_CPUS;
281                 }
282                 gTSSs = gaTSSs;
283         }
284         else {
285                 Log("No MP Table was found, assuming uniprocessor\n");
286                 giNumCPUs = 1;
287                 gTSSs = &gTSS0;
288         }
289         #else
290         giNumCPUs = 1;
291         gTSSs = &gTSS0;
292         MM_FinishVirtualInit();
293         #endif
294         
295         #if 0
296         // Initialise Double Fault TSS
297         gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF;
298         gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16;
299         gGDT[5].BaseHi = (Uint)&gDoubleFault_TSS >> 24;
300         
301         // Set double fault IDT to use the new TSS
302         gIDT[8].OffsetLo = 0;
303         gIDT[8].CS = 5<<3;
304         gIDT[8].Flags = 0x8500;
305         gIDT[8].OffsetHi = 0;
306         #endif
307         
308         // Set timer frequency
309         outb(0x43, 0x34);       // Set Channel 0, Low/High, Rate Generator
310         outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
311         outb(0x40, (TIMER_DIVISOR>>8)&0xFF);    // High Byte
312         
313         #if USE_MP
314         // Get the count setting for APIC timer
315         Log("Determining APIC Count");
316         __asm__ __volatile__ ("sti");
317         while( giMP_TimerCount == 0 )   __asm__ __volatile__ ("hlt");
318         __asm__ __volatile__ ("cli");
319         Log("APIC Count %i", giMP_TimerCount);
320         {
321                 Uint64  freq = giMP_TimerCount;
322                 freq /= TIMER_DIVISOR;
323                 freq *= TIMER_BASE;
324                 if( (freq /= 1000) < 2*1000)
325                         Log("Bus Frequency %i KHz", freq);
326                 else if( (freq /= 1000) < 2*1000)
327                         Log("Bus Frequency %i MHz", freq);
328                 else if( (freq /= 1000) < 2*1000)
329                         Log("Bus Frequency %i GHz", freq);
330                 else
331                         Log("Bus Frequency %i THz", freq);
332         }
333         
334         // Initialise Normal TSS(s)
335         for(pos=0;pos<giNumCPUs;pos++)
336         {
337         #else
338         pos = 0;
339         #endif
340                 gTSSs[pos].SS0 = 0x10;
341                 gTSSs[pos].ESP0 = 0;    // Set properly by scheduler
342                 gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
343                 gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos])) >> 16;
344                 gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
345         #if USE_MP
346         }
347         #endif
348         
349         // Load the BSP's TSS
350         __asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
351         
352         #if USE_MP
353         gaCPUs[0].Current = &gThreadZero;
354         #else
355         gCurrentThread = &gThreadZero;
356         #endif
357         gThreadZero.CurCPU = 0;
358         
359         #if USE_PAE
360         gThreadZero.MemState.PDP[0] = 0;
361         gThreadZero.MemState.PDP[1] = 0;
362         gThreadZero.MemState.PDP[2] = 0;
363         #else
364         gThreadZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE;
365         #endif
366         
367         // Create Per-Process Data Block
368         MM_Allocate(MM_PPD_CFG);
369         
370         // Change Stacks
371         Proc_ChangeStack();
372 }
373
374 #if USE_MP
375 void MP_StartAP(int CPU)
376 {
377         Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
378         
379         // Set location of AP startup code and mark for a warm restart
380         *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
381         *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
382         outb(0x70, 0x0F);       outb(0x71, 0x0A);       // Warm Reset
383         MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);   // Init IPI
384         
385         // Delay
386         inb(0x80); inb(0x80); inb(0x80); inb(0x80);
387         
388         // TODO: Use a better address, preferably registered with the MM
389         // - MM_AllocDMA mabye?
390         // Create a far jump
391         *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA;  // Far JMP
392         *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);     // IP
393         *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF;       // CS
394         // Send a Startup-IPI to make the CPU execute at 0x11000 (which we
395         // just filled)
396         MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6);        // StartupIPI
397         
398         giNumInitingCPUs ++;
399 }
400
401 /**
402  * \brief Send an Inter-Processor Interrupt
403  * \param APICID        Processor's Local APIC ID
404  * \param Vector        Argument of some kind
405  * \param DeliveryMode  Type of signal?
406  */
407 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
408 {
409         Uint32  val;
410         
411         // Hi
412         val = (Uint)APICID << 24;
413         Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
414         gpMP_LocalAPIC->ICR[1].Val = val;
415         // Low (and send)
416         val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
417         Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
418         gpMP_LocalAPIC->ICR[0].Val = val;
419 }
420 #endif
421
422 /**
423  * \fn void Proc_Start(void)
424  * \brief Start process scheduler
425  */
426 void Proc_Start(void)
427 {
428         #if USE_MP
429          int    i;
430         #endif
431         
432         #if USE_MP
433         // Start APs
434         for( i = 0; i < giNumCPUs; i ++ )
435         {
436                 // Create Idle Task
437                 if(Proc_Clone(0, 0) == 0)
438                 {
439                         gaCPUs[i].IdleThread = Proc_GetCurThread();
440                         gaCPUs[i].IdleThread->ThreadName = "Idle Thread";
441                         gaCPUs[i].IdleThread->NumTickets = 0;   // Never called randomly
442                         gaCPUs[i].IdleThread->Quantum = 1;      // 1 slice quantum
443                         for(;;) HALT(); // Just yeilds
444                 }
445                 gaCPUs[i].Current = NULL;
446                 
447                 // Start the AP
448                 if( i != giProc_BootProcessorID ) {
449                         MP_StartAP( i );
450                 }
451         }
452         
453         // BSP still should run the current task
454         gaCPUs[0].Current = &gThreadZero;
455         
456         // Start interrupts and wait for APs to come up
457         Log("Waiting for APs to come up\n");
458         __asm__ __volatile__ ("sti");
459         while( giNumInitingCPUs )       __asm__ __volatile__ ("hlt");
460         MM_FinishVirtualInit();
461         #else
462         // Create Idle Task
463         if(Proc_Clone(0, 0) == 0)
464         {
465                 gpIdleThread = Proc_GetCurThread();
466                 gpIdleThread->ThreadName = "Idle Thread";
467                 gpIdleThread->NumTickets = 0;   // Never called randomly
468                 gpIdleThread->Quantum = 1;      // 1 slice quantum
469                 for(;;) HALT(); // Just yeilds
470         }
471         
472         // Set current task
473         gCurrentThread = &gThreadZero;
474         
475         // Start Interrupts (and hence scheduler)
476         __asm__ __volatile__("sti");
477         #endif
478 }
479
480 /**
481  * \fn tThread *Proc_GetCurThread(void)
482  * \brief Gets the current thread
483  */
484 tThread *Proc_GetCurThread(void)
485 {
486         #if USE_MP
487         //return gaCPUs[ gaAPIC_to_CPU[gpMP_LocalAPIC->ID.Val&0xFF] ].Current;
488         return gaCPUs[ GetCPUNum() ].Current;
489         #else
490         return gCurrentThread;
491         #endif
492 }
493
494 /**
495  * \fn void Proc_ChangeStack(void)
496  * \brief Swaps the current stack for a new one (in the proper stack reigon)
497  */
498 void Proc_ChangeStack(void)
499 {
500         Uint    esp, ebp;
501         Uint    tmpEbp, oldEsp;
502         Uint    curBase, newBase;
503
504         __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
505         __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
506
507         oldEsp = esp;
508
509         // Create new KStack
510         newBase = MM_NewKStack();
511         // Check for errors
512         if(newBase == 0) {
513                 Panic("What the?? Unable to allocate space for initial kernel stack");
514                 return;
515         }
516
517         curBase = (Uint)&Kernel_Stack_Top;
518         
519         LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
520
521         // Get ESP as a used size
522         esp = curBase - esp;
523         LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp );
524         // Copy used stack
525         memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp );
526         // Get ESP as an offset in the new stack
527         esp = newBase - esp;
528         // Adjust EBP
529         ebp = newBase - (curBase - ebp);
530
531         // Repair EBPs & Stack Addresses
532         // Catches arguments also, but may trash stack-address-like values
533         for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4)
534         {
535                 if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase)
536                         *(Uint*)tmpEbp += newBase - curBase;
537         }
538         
539         Proc_GetCurThread()->KernelStack = newBase;
540         
541         __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp));
542         __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp));
543 }
544
545 /**
546  * \fn int Proc_Clone(Uint *Err, Uint Flags)
547  * \brief Clone the current process
548  */
549 int Proc_Clone(Uint *Err, Uint Flags)
550 {
551         tThread *newThread;
552         tThread *cur = Proc_GetCurThread();
553         Uint    eip, esp, ebp;
554         
555         __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp));
556         __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp));
557         
558         newThread = Threads_CloneTCB(Err, Flags);
559         if(!newThread)  return -1;
560         
561         // Initialise Memory Space (New Addr space or kernel stack)
562         if(Flags & CLONE_VM) {
563                 newThread->MemState.CR3 = MM_Clone();
564                 newThread->KernelStack = cur->KernelStack;
565         } else {
566                 Uint    tmpEbp, oldEsp = esp;
567
568                 // Set CR3
569                 newThread->MemState.CR3 = cur->MemState.CR3;
570
571                 // Create new KStack
572                 newThread->KernelStack = MM_NewKStack();
573                 // Check for errors
574                 if(newThread->KernelStack == 0) {
575                         free(newThread);
576                         return -1;
577                 }
578
579                 // Get ESP as a used size
580                 esp = cur->KernelStack - esp;
581                 // Copy used stack
582                 memcpy( (void*)(newThread->KernelStack - esp), (void*)(cur->KernelStack - esp), esp );
583                 // Get ESP as an offset in the new stack
584                 esp = newThread->KernelStack - esp;
585                 // Adjust EBP
586                 ebp = newThread->KernelStack - (cur->KernelStack - ebp);
587
588                 // Repair EBPs & Stack Addresses
589                 // Catches arguments also, but may trash stack-address-like values
590                 for(tmpEbp = esp; tmpEbp < newThread->KernelStack; tmpEbp += 4)
591                 {
592                         if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < cur->KernelStack)
593                                 *(Uint*)tmpEbp += newThread->KernelStack - cur->KernelStack;
594                 }
595         }
596         
597         // Save core machine state
598         newThread->SavedState.ESP = esp;
599         newThread->SavedState.EBP = ebp;
600         eip = GetEIP();
601         if(eip == SWITCH_MAGIC) {
602                 outb(0x20, 0x20);       // ACK Timer and return as child
603                 __asm__ __volatile__ ("sti");   // Restart interrupts
604                 return 0;
605         }
606         
607         // Set EIP as parent
608         newThread->SavedState.EIP = eip;
609         
610         // Lock list and add to active
611         Threads_AddActive(newThread);
612         
613         return newThread->TID;
614 }
615
616 /**
617  * \fn int Proc_SpawnWorker(void)
618  * \brief Spawns a new worker thread
619  */
620 int Proc_SpawnWorker(void)
621 {
622         tThread *new, *cur;
623         Uint    eip, esp, ebp;
624         
625         cur = Proc_GetCurThread();
626         
627         // Create new thread
628         new = malloc( sizeof(tThread) );
629         if(!new) {
630                 Warning("Proc_SpawnWorker - Out of heap space!\n");
631                 return -1;
632         }
633         memcpy(new, &gThreadZero, sizeof(tThread));
634         // Set Thread ID
635         new->TID = giNextTID++;
636         // Create a new worker stack (in PID0's address space)
637         // The stack is relocated by this code
638         new->KernelStack = MM_NewWorkerStack();
639
640         // Get ESP and EBP based in the new stack
641         __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp));
642         __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp));
643         esp = new->KernelStack - (cur->KernelStack - esp);
644         ebp = new->KernelStack - (cur->KernelStack - ebp);      
645         
646         // Save core machine state
647         new->SavedState.ESP = esp;
648         new->SavedState.EBP = ebp;
649         eip = GetEIP();
650         if(eip == SWITCH_MAGIC) {
651                 outb(0x20, 0x20);       // ACK Timer and return as child
652                 return 0;
653         }
654         
655         // Set EIP as parent
656         new->SavedState.EIP = eip;
657         // Mark as active
658         new->Status = THREAD_STAT_ACTIVE;
659         Threads_AddActive( new );
660         
661         return new->TID;
662 }
663
664 /**
665  * \fn Uint Proc_MakeUserStack(void)
666  * \brief Creates a new user stack
667  */
668 Uint Proc_MakeUserStack(void)
669 {
670          int    i;
671         Uint    base = USER_STACK_TOP - USER_STACK_SZ;
672         
673         // Check Prospective Space
674         for( i = USER_STACK_SZ >> 12; i--; )
675                 if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
676                         break;
677         
678         if(i != -1)     return 0;
679         
680         // Allocate Stack - Allocate incrementally to clean up MM_Dump output
681         for( i = 0; i < USER_STACK_SZ/0x1000; i++ )
682                 MM_Allocate( base + (i<<12) );
683         
684         return base + USER_STACK_SZ;
685 }
686
687 /**
688  * \fn void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
689  * \brief Starts a user task
690  */
691 void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
692 {
693         Uint    *stack = (void*)Proc_MakeUserStack();
694          int    i;
695         Uint    delta;
696         Uint16  ss, cs;
697         
698         //Log("stack = %p", stack);
699         
700         // Copy Arguments
701         stack -= DataSize/sizeof(*stack);
702         memcpy( stack, ArgV, DataSize );
703         
704         //Log("stack = %p", stack);
705         
706         if( DataSize )
707         {
708                 // Adjust Arguments and environment
709                 delta = (Uint)stack - (Uint)ArgV;
710                 ArgV = (char**)stack;
711                 for( i = 0; ArgV[i]; i++ )
712                         ArgV[i] += delta;
713                 i ++;
714                 
715                 // Do we care about EnvP?
716                 if( EnvP ) {
717                         EnvP = &ArgV[i];
718                         for( i = 0; EnvP[i]; i++ )
719                                 EnvP[i] += delta;
720                 }
721         }
722         
723         // User Mode Segments
724         ss = 0x23;      cs = 0x1B;
725         
726         // Arguments
727         *--stack = (Uint)EnvP;
728         *--stack = (Uint)ArgV;
729         *--stack = (Uint)ArgC;
730         while(*Bases)
731                 *--stack = *Bases++;
732         *--stack = 0;   // Return Address
733         
734         Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
735 }
736
737 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
738 {
739         Uint    *stack = (void*)Stack;
740         *--stack = SS;          //Stack Segment
741         *--stack = Stack;       //Stack Pointer
742         *--stack = Flags;       //EFLAGS (Resvd (0x2) and IF (0x20))
743         *--stack = CS;          //Code Segment
744         *--stack = IP;  //EIP
745         //PUSHAD
746         *--stack = 0xAAAAAAAA;  // eax
747         *--stack = 0xCCCCCCCC;  // ecx
748         *--stack = 0xDDDDDDDD;  // edx
749         *--stack = 0xBBBBBBBB;  // ebx
750         *--stack = 0xD1D1D1D1;  // edi
751         *--stack = 0x54545454;  // esp - NOT POPED
752         *--stack = 0x51515151;  // esi
753         *--stack = 0xB4B4B4B4;  // ebp
754         //Individual PUSHs
755         *--stack = SS;  // ds
756         *--stack = SS;  // es
757         *--stack = SS;  // fs
758         *--stack = SS;  // gs
759         
760         __asm__ __volatile__ (
761         "mov %%eax,%%esp;\n\t"  // Set stack pointer
762         "pop %%gs;\n\t"
763         "pop %%fs;\n\t"
764         "pop %%es;\n\t"
765         "pop %%ds;\n\t"
766         "popa;\n\t"
767         "iret;\n\t" : : "a" (stack));
768         for(;;);
769 }
770
771 /**
772  * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
773  * \brief Demotes a process to a lower permission level
774  * \param Err   Pointer to user's errno
775  * \param Dest  New Permission Level
776  * \param Regs  Pointer to user's register structure
777  */
778 int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
779 {
780          int    cpl = Regs->cs & 3;
781         // Sanity Check
782         if(Dest > 3 || Dest < 0) {
783                 *Err = -EINVAL;
784                 return -1;
785         }
786         
787         // Permission Check
788         if(cpl > Dest) {
789                 *Err = -EACCES;
790                 return -1;
791         }
792         
793         // Change the Segment Registers
794         Regs->cs = (((Dest+1)<<4) | Dest) - 8;
795         Regs->ss = ((Dest+1)<<4) | Dest;
796         // Check if the GP Segs are GDT, then change them
797         if(!(Regs->ds & 4))     Regs->ds = ((Dest+1)<<4) | Dest;
798         if(!(Regs->es & 4))     Regs->es = ((Dest+1)<<4) | Dest;
799         if(!(Regs->fs & 4))     Regs->fs = ((Dest+1)<<4) | Dest;
800         if(!(Regs->gs & 4))     Regs->gs = ((Dest+1)<<4) | Dest;
801         
802         return 0;
803 }
804
805 /**
806  * \brief Calls a signal handler in user mode
807  * \note Used for signals
808  */
809 void Proc_CallFaultHandler(tThread *Thread)
810 {
811         // Rewinds the stack and calls the user function
812         // Never returns
813         __asm__ __volatile__ ("mov %0, %%ebp;\n\tcall Proc_ReturnToUser" :: "r"(Thread->FaultHandler));
814         for(;;);
815 }
816
817 /**
818  * \fn void Proc_Scheduler(int CPU)
819  * \brief Swap current thread and clears dead threads
820  */
821 void Proc_Scheduler(int CPU)
822 {
823         Uint    esp, ebp, eip;
824         tThread *thread;
825         
826         // If the spinlock is set, let it complete
827         if(IS_LOCKED(&glThreadListLock))        return;
828         
829         // Clear Delete Queue
830         while(gDeleteThreads)
831         {
832                 thread = gDeleteThreads->Next;
833                 if(gDeleteThreads->IsLocked) {  // Only free if structure is unused
834                         gDeleteThreads->Status = THREAD_STAT_NULL;
835                         free( gDeleteThreads );
836                 }
837                 gDeleteThreads = thread;
838         }
839         
840         // Check if there is any tasks running
841         if(giNumActiveThreads == 0) {
842                 #if 0
843                 Log("No Active threads, sleeping");
844                 #endif
845                 #if USE_MP
846                 if(CPU)
847                         gpMP_LocalAPIC->EOI.Val = 0;
848                 else
849                 #endif
850                         outb(0x20, 0x20);
851                 __asm__ __volatile__ ("hlt");
852                 return;
853         }
854         
855         // Get current thread
856         #if USE_MP
857         thread = gaCPUs[CPU].Current;
858         #else
859         thread = gCurrentThread;
860         #endif
861         
862         if( thread )
863         {
864                 // Reduce remaining quantum and continue timeslice if non-zero
865                 if(thread->Remaining--) return;
866                 // Reset quantum for next call
867                 thread->Remaining = thread->Quantum;
868                 
869                 // Get machine state
870                 __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
871                 __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
872                 eip = GetEIP();
873                 if(eip == SWITCH_MAGIC) return; // Check if a switch happened
874                 
875                 // Save machine state
876                 thread->SavedState.ESP = esp;
877                 thread->SavedState.EBP = ebp;
878                 thread->SavedState.EIP = eip;
879         }
880         
881         // Get next thread to run
882         thread = Threads_GetNextToRun(CPU, thread);
883         
884         // No avaliable tasks, just go into low power mode
885         if(thread == NULL) {
886                 //HALT();
887                 //return;
888                 #if USE_MP
889                 thread = gaCPUs[CPU].IdleThread;
890                 #else
891                 thread = gpIdleThread;
892                 #endif
893         }
894         
895         #if DEBUG_TRACE_SWITCH
896         Log("Switching to task %i, CR3 = 0x%x, EIP = %p",
897                 thread->TID,
898                 thread->MemState.CR3,
899                 thread->SavedState.EIP
900                 );
901         #endif
902         
903         // Set current thread
904         #if USE_MP
905         gaCPUs[CPU].Current = thread;
906         #else
907         gCurrentThread = thread;
908         #endif
909         
910         //Log("CPU = %i", CPU);
911         
912         // Update Kernel Stack pointer
913         gTSSs[CPU].ESP0 = thread->KernelStack-4;
914         
915         // Set address space
916         #if USE_PAE
917         # error "Todo: Implement PAE Address space switching"
918         #else
919                 __asm__ __volatile__ ("mov %0, %%cr3"::"a"(thread->MemState.CR3));
920         #endif
921         
922         #if 0
923         if(thread->SavedState.ESP > 0xC0000000
924         && thread->SavedState.ESP < thread->KernelStack-0x2000) {
925                 Log_Warning("Proc", "Possible bad ESP %p (PID %i)", thread->SavedState.ESP);
926         }
927         #endif
928         
929         // Switch threads
930         __asm__ __volatile__ (
931                 "mov %1, %%esp\n\t"     // Restore ESP
932                 "mov %2, %%ebp\n\t"     // and EBP
933                 "jmp *%3" : :   // And return to where we saved state (Proc_Clone or Proc_Scheduler)
934                 "a"(SWITCH_MAGIC), "b"(thread->SavedState.ESP),
935                 "d"(thread->SavedState.EBP), "c"(thread->SavedState.EIP)
936                 );
937         for(;;);        // Shouldn't reach here
938 }
939
940 // === EXPORTS ===
941 EXPORT(Proc_SpawnWorker);

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