Misc changes
[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 #define DEBUG_DISABLE_DOUBLEFAULT       1
18
19 // === CONSTANTS ===
20 #define SWITCH_MAGIC    0xFF5317C8      // FF SWITCH - There is no code in this area
21 // Base is 1193182
22 #define TIMER_BASE      1193182
23 #define TIMER_DIVISOR   11932   //~100Hz
24
25 // === TYPES ===
26 #if USE_MP
27 typedef struct sCPU
28 {
29         Uint8   APICID;
30         Uint8   State;  // 0: Unavaliable, 1: Idle, 2: Active
31         Uint16  Resvd;
32         tThread *Current;
33         tThread *IdleThread;
34 }       tCPU;
35 #endif
36
37 // === IMPORTS ===
38 extern tGDT     gGDT[];
39 extern tIDT     gIDT[];
40 extern void APWait(void);       // 16-bit AP pause code
41 extern void APStartup(void);    // 16-bit AP startup code
42 extern Uint     GetEIP(void);   // start.asm
43 extern int      GetCPUNum(void);        // start.asm
44 extern Uint32   gaInitPageDir[1024];    // start.asm
45 extern char     Kernel_Stack_Top[];
46 extern tShortSpinlock   glThreadListLock;
47 extern int      giNumCPUs;
48 extern int      giNextTID;
49 extern tThread  gThreadZero;
50 extern void     Isr8(void);     // Double Fault
51 extern void     Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack);
52 extern void     scheduler_return;       // Return address in SchedulerBase
53 extern void     IRQCommon;      // Common IRQ handler code
54
55 // === PROTOTYPES ===
56 void    ArchThreads_Init(void);
57 #if USE_MP
58 void    MP_StartAP(int CPU);
59 void    MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
60 #endif
61 //void  Proc_Start(void);
62 //tThread       *Proc_GetCurThread(void);
63 void    Proc_ChangeStack(void);
64 // int  Proc_Clone(Uint *Err, Uint Flags);
65 Uint    Proc_MakeUserStack(void);
66 void    Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
67 void    Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP);
68  int    Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
69 void    Proc_CallFaultHandler(tThread *Thread);
70 void    Proc_DumpThreadCPUState(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 !DEBUG_DISABLE_DOUBLEFAULT
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         Log("Timer Frequency %i.%03i Hz",
314                 TIMER_BASE/TIMER_DIVISOR,
315                 ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000
316                 );
317         
318         #if USE_MP
319         // Get the count setting for APIC timer
320         Log("Determining APIC Count");
321         __asm__ __volatile__ ("sti");
322         while( giMP_TimerCount == 0 )   __asm__ __volatile__ ("hlt");
323         __asm__ __volatile__ ("cli");
324         Log("APIC Count %i", giMP_TimerCount);
325         {
326                 Uint64  freq = giMP_TimerCount;
327                 freq /= TIMER_DIVISOR;
328                 freq *= TIMER_BASE;
329                 if( (freq /= 1000) < 2*1000)
330                         Log("Bus Frequency %i KHz", freq);
331                 else if( (freq /= 1000) < 2*1000)
332                         Log("Bus Frequency %i MHz", freq);
333                 else if( (freq /= 1000) < 2*1000)
334                         Log("Bus Frequency %i GHz", freq);
335                 else
336                         Log("Bus Frequency %i THz", freq);
337         }
338         
339         // Initialise Normal TSS(s)
340         for(pos=0;pos<giNumCPUs;pos++)
341         {
342         #else
343         pos = 0;
344         #endif
345                 gTSSs[pos].SS0 = 0x10;
346                 gTSSs[pos].ESP0 = 0;    // Set properly by scheduler
347                 gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
348                 gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos]) >> 16) & 0xFFFF;
349                 gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
350         #if USE_MP
351         }
352         #endif
353         
354         // Load the BSP's TSS
355         __asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
356         // Set Current Thread and CPU Number in DR0 and DR1
357         __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero));
358         __asm__ __volatile__ ("mov %0, %%db1"::"r"(0));
359         
360         #if USE_MP
361         gaCPUs[0].Current = &gThreadZero;
362         #else
363         gCurrentThread = &gThreadZero;
364         #endif
365         gThreadZero.CurCPU = 0;
366         
367         #if USE_PAE
368         gThreadZero.MemState.PDP[0] = 0;
369         gThreadZero.MemState.PDP[1] = 0;
370         gThreadZero.MemState.PDP[2] = 0;
371         #else
372         gThreadZero.MemState.CR3 = (Uint)gaInitPageDir - KERNEL_BASE;
373         #endif
374         
375         // Create Per-Process Data Block
376         if( !MM_Allocate(MM_PPD_CFG) )
377         {
378                 Panic("OOM - No space for initiali Per-Process Config");
379         }
380         
381         // Change Stacks
382         Proc_ChangeStack();
383 }
384
385 #if USE_MP
386 void MP_StartAP(int CPU)
387 {
388         Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
389         
390         // Set location of AP startup code and mark for a warm restart
391         *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
392         *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
393         outb(0x70, 0x0F);       outb(0x71, 0x0A);       // Set warm reset flag
394         MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);   // Init IPI
395         
396         // Delay
397         inb(0x80); inb(0x80); inb(0x80); inb(0x80);
398         
399         // TODO: Use a better address, preferably registered with the MM
400         // - MM_AllocDMA mabye?
401         // Create a far jump
402         *(Uint8*)(KERNEL_BASE|0x11000) = 0xEA;  // Far JMP
403         *(Uint16*)(KERNEL_BASE|0x11001) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);     // IP
404         *(Uint16*)(KERNEL_BASE|0x11003) = 0xFFFF;       // CS
405         // Send a Startup-IPI to make the CPU execute at 0x11000 (which we
406         // just filled)
407         MP_SendIPI(gaCPUs[CPU].APICID, 0x11, 6);        // StartupIPI
408         
409         giNumInitingCPUs ++;
410 }
411
412 /**
413  * \brief Send an Inter-Processor Interrupt
414  * \param APICID        Processor's Local APIC ID
415  * \param Vector        Argument of some kind
416  * \param DeliveryMode  Type of signal?
417  */
418 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
419 {
420         Uint32  val;
421         
422         // Hi
423         val = (Uint)APICID << 24;
424         Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[1], val);
425         gpMP_LocalAPIC->ICR[1].Val = val;
426         // Low (and send)
427         val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
428         Log("*%p = 0x%08x", &gpMP_LocalAPIC->ICR[0], val);
429         gpMP_LocalAPIC->ICR[0].Val = val;
430 }
431 #endif
432
433 /**
434  * \fn void Proc_Start(void)
435  * \brief Start process scheduler
436  */
437 void Proc_Start(void)
438 {
439         #if USE_MP
440          int    i;
441         #endif
442         
443         #if USE_MP
444         // Start APs
445         for( i = 0; i < giNumCPUs; i ++ )
446         {
447                  int    tid;
448                 if(i)   gaCPUs[i].Current = NULL;
449                 
450                 // Create Idle Task
451                 if( (tid = Proc_Clone(0, 0)) == 0)
452                 {
453                         for(;;) HALT(); // Just yeilds
454                 }
455                 gaCPUs[i].IdleThread = Threads_GetThread(tid);
456                 gaCPUs[i].IdleThread->ThreadName = (char*)"Idle Thread";
457                 Threads_SetPriority( gaCPUs[i].IdleThread, -1 );        // Never called randomly
458                 gaCPUs[i].IdleThread->Quantum = 1;      // 1 slice quantum
459                 
460                 
461                 // Start the AP
462                 if( i != giProc_BootProcessorID ) {
463                         MP_StartAP( i );
464                 }
465         }
466         
467         // BSP still should run the current task
468         gaCPUs[0].Current = &gThreadZero;
469         
470         // Start interrupts and wait for APs to come up
471         Log("Waiting for APs to come up\n");
472         __asm__ __volatile__ ("sti");
473         while( giNumInitingCPUs )       __asm__ __volatile__ ("hlt");
474         #else
475         // Create Idle Task
476         if(Proc_Clone(0, 0) == 0)
477         {
478                 gpIdleThread = Proc_GetCurThread();
479                 gpIdleThread->ThreadName = strdup("Idle Thread");
480                 Threads_SetPriority( gpIdleThread, -1 );        // Never called randomly
481                 gpIdleThread->Quantum = 1;      // 1 slice quantum
482                 for(;;) HALT(); // Just yeilds
483         }
484         
485         // Set current task
486         gCurrentThread = &gThreadZero;
487         
488         // Start Interrupts (and hence scheduler)
489         __asm__ __volatile__("sti");
490         #endif
491         MM_FinishVirtualInit();
492 }
493
494 /**
495  * \fn tThread *Proc_GetCurThread(void)
496  * \brief Gets the current thread
497  */
498 tThread *Proc_GetCurThread(void)
499 {
500         #if USE_MP
501         return gaCPUs[ GetCPUNum() ].Current;
502         #else
503         return gCurrentThread;
504         #endif
505 }
506
507 /**
508  * \fn void Proc_ChangeStack(void)
509  * \brief Swaps the current stack for a new one (in the proper stack reigon)
510  */
511 void Proc_ChangeStack(void)
512 {
513         Uint    esp, ebp;
514         Uint    tmpEbp, oldEsp;
515         Uint    curBase, newBase;
516
517         __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
518         __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
519
520         oldEsp = esp;
521
522         // Create new KStack
523         newBase = MM_NewKStack();
524         // Check for errors
525         if(newBase == 0) {
526                 Panic("What the?? Unable to allocate space for initial kernel stack");
527                 return;
528         }
529
530         curBase = (Uint)&Kernel_Stack_Top;
531         
532         LOG("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
533
534         // Get ESP as a used size
535         esp = curBase - esp;
536         LOG("memcpy( %p, %p, 0x%x )", (void*)(newBase - esp), (void*)(curBase - esp), esp );
537         // Copy used stack
538         memcpy( (void*)(newBase - esp), (void*)(curBase - esp), esp );
539         // Get ESP as an offset in the new stack
540         esp = newBase - esp;
541         // Adjust EBP
542         ebp = newBase - (curBase - ebp);
543
544         // Repair EBPs & Stack Addresses
545         // Catches arguments also, but may trash stack-address-like values
546         for(tmpEbp = esp; tmpEbp < newBase; tmpEbp += 4)
547         {
548                 if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < curBase)
549                         *(Uint*)tmpEbp += newBase - curBase;
550         }
551         
552         Proc_GetCurThread()->KernelStack = newBase;
553         
554         __asm__ __volatile__ ("mov %0, %%esp"::"r"(esp));
555         __asm__ __volatile__ ("mov %0, %%ebp"::"r"(ebp));
556 }
557
558 /**
559  * \fn int Proc_Clone(Uint *Err, Uint Flags)
560  * \brief Clone the current process
561  */
562 int Proc_Clone(Uint *Err, Uint Flags)
563 {
564         tThread *newThread;
565         tThread *cur = Proc_GetCurThread();
566         Uint    eip, esp, ebp;
567         
568         __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp));
569         __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp));
570         
571         newThread = Threads_CloneTCB(Err, Flags);
572         if(!newThread)  return -1;
573         
574         // Initialise Memory Space (New Addr space or kernel stack)
575         if(Flags & CLONE_VM) {
576                 newThread->MemState.CR3 = MM_Clone();
577                 // Check for errors
578                 if(newThread->MemState.CR3 == 0) {
579                         Threads_Kill(newThread, -2);
580                         return -1;
581                 }
582                 newThread->KernelStack = cur->KernelStack;
583         } else {
584                 Uint    tmpEbp, oldEsp = esp;
585
586                 // Set CR3
587                 #if USE_PAE
588                 # warning "PAE Unimplemented"
589                 #else
590                 newThread->MemState.CR3 = cur->MemState.CR3;
591                 #endif
592
593                 // Create new KStack
594                 newThread->KernelStack = MM_NewKStack();
595                 // Check for errors
596                 if(newThread->KernelStack == 0) {
597                         Threads_Kill(newThread, -2);
598                         return -1;
599                 }
600
601                 // Get ESP as a used size
602                 esp = cur->KernelStack - esp;
603                 // Copy used stack
604                 memcpy( (void*)(newThread->KernelStack - esp), (void*)(cur->KernelStack - esp), esp );
605                 // Get ESP as an offset in the new stack
606                 esp = newThread->KernelStack - esp;
607                 // Adjust EBP
608                 ebp = newThread->KernelStack - (cur->KernelStack - ebp);
609
610                 // Repair EBPs & Stack Addresses
611                 // Catches arguments also, but may trash stack-address-like values
612                 for(tmpEbp = esp; tmpEbp < newThread->KernelStack; tmpEbp += 4)
613                 {
614                         if(oldEsp < *(Uint*)tmpEbp && *(Uint*)tmpEbp < cur->KernelStack)
615                                 *(Uint*)tmpEbp += newThread->KernelStack - cur->KernelStack;
616                 }
617         }
618         
619         // Save core machine state
620         newThread->SavedState.ESP = esp;
621         newThread->SavedState.EBP = ebp;
622         eip = GetEIP();
623         if(eip == SWITCH_MAGIC) {
624                 __asm__ __volatile__ ("mov %0, %%db0" : : "r" (newThread) );
625                 #if USE_MP
626                 // ACK the interrupt
627                 if( GetCPUNum() )
628                         gpMP_LocalAPIC->EOI.Val = 0;
629                 else
630                 #endif
631                         outb(0x20, 0x20);       // ACK Timer and return as child
632                 __asm__ __volatile__ ("sti");   // Restart interrupts
633                 return 0;
634         }
635         
636         // Set EIP as parent
637         newThread->SavedState.EIP = eip;
638         
639         // Lock list and add to active
640         Threads_AddActive(newThread);
641         
642         return newThread->TID;
643 }
644
645 /**
646  * \fn int Proc_SpawnWorker(void)
647  * \brief Spawns a new worker thread
648  */
649 int Proc_SpawnWorker(void)
650 {
651         tThread *new, *cur;
652         Uint    eip, esp, ebp;
653         
654         cur = Proc_GetCurThread();
655         
656         // Create new thread
657         new = Threads_CloneThreadZero();
658         if(!new) {
659                 Warning("Proc_SpawnWorker - Out of heap space!\n");
660                 return -1;
661         }
662         // Create a new worker stack (in PID0's address space)
663         // - The stack is relocated by this function
664         new->KernelStack = MM_NewWorkerStack();
665
666         // Get ESP and EBP based in the new stack
667         __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp));
668         __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp));
669         esp = new->KernelStack - (cur->KernelStack - esp);
670         ebp = new->KernelStack - (cur->KernelStack - ebp);      
671         
672         // Save core machine state
673         new->SavedState.ESP = esp;
674         new->SavedState.EBP = ebp;
675         eip = GetEIP();
676         if(eip == SWITCH_MAGIC) {
677                 __asm__ __volatile__ ("mov %0, %%db0" : : "r"(new));
678                 #if USE_MP
679                 // ACK the interrupt
680                 if(GetCPUNum())
681                         gpMP_LocalAPIC->EOI.Val = 0;
682                 else
683                 #endif
684                         outb(0x20, 0x20);       // ACK Timer and return as child
685                 __asm__ __volatile__ ("sti");   // Restart interrupts
686                 return 0;
687         }
688         
689         // Set EIP as parent
690         new->SavedState.EIP = eip;
691         // Mark as active
692         Threads_AddActive( new );
693         
694         return new->TID;
695 }
696
697 /**
698  * \fn Uint Proc_MakeUserStack(void)
699  * \brief Creates a new user stack
700  */
701 Uint Proc_MakeUserStack(void)
702 {
703          int    i;
704         Uint    base = USER_STACK_TOP - USER_STACK_SZ;
705         
706         // Check Prospective Space
707         for( i = USER_STACK_SZ >> 12; i--; )
708                 if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
709                         break;
710         
711         if(i != -1)     return 0;
712         
713         // Allocate Stack - Allocate incrementally to clean up MM_Dump output
714         for( i = 0; i < USER_STACK_SZ/0x1000; i++ )
715         {
716                 if( !MM_Allocate( base + (i<<12) ) )
717                 {
718                         Warning("OOM: Proc_MakeUserStack");
719                         return 0;
720                 }
721         }
722         
723         return base + USER_STACK_SZ;
724 }
725
726 /**
727  * \fn void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
728  * \brief Starts a user task
729  */
730 void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
731 {
732         Uint    *stack = (void*)Proc_MakeUserStack();
733          int    i;
734         Uint    delta;
735         Uint16  ss, cs;
736         
737         //Log("stack = %p", stack);
738         
739         // Copy Arguments
740         stack -= DataSize/sizeof(*stack);
741         memcpy( stack, ArgV, DataSize );
742         
743         //Log("stack = %p", stack);
744         
745         if( DataSize )
746         {
747                 // Adjust Arguments and environment
748                 delta = (Uint)stack - (Uint)ArgV;
749                 ArgV = (char**)stack;
750                 for( i = 0; ArgV[i]; i++ )
751                         ArgV[i] += delta;
752                 i ++;
753                 
754                 // Do we care about EnvP?
755                 if( EnvP ) {
756                         EnvP = &ArgV[i];
757                         for( i = 0; EnvP[i]; i++ )
758                                 EnvP[i] += delta;
759                 }
760         }
761         
762         // User Mode Segments
763         ss = 0x23;      cs = 0x1B;
764         
765         // Arguments
766         *--stack = (Uint)EnvP;
767         *--stack = (Uint)ArgV;
768         *--stack = (Uint)ArgC;
769         while(*Bases)
770                 *--stack = *Bases++;
771         *--stack = 0;   // Return Address
772         
773         Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
774 }
775
776 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
777 {
778         Uint    *stack = (void*)Stack;
779         *--stack = SS;          //Stack Segment
780         *--stack = Stack;       //Stack Pointer
781         *--stack = Flags;       //EFLAGS (Resvd (0x2) and IF (0x20))
782         *--stack = CS;          //Code Segment
783         *--stack = IP;  //EIP
784         //PUSHAD
785         *--stack = 0xAAAAAAAA;  // eax
786         *--stack = 0xCCCCCCCC;  // ecx
787         *--stack = 0xDDDDDDDD;  // edx
788         *--stack = 0xBBBBBBBB;  // ebx
789         *--stack = 0xD1D1D1D1;  // edi
790         *--stack = 0x54545454;  // esp - NOT POPED
791         *--stack = 0x51515151;  // esi
792         *--stack = 0xB4B4B4B4;  // ebp
793         //Individual PUSHs
794         *--stack = SS;  // ds
795         *--stack = SS;  // es
796         *--stack = SS;  // fs
797         *--stack = SS;  // gs
798         
799         __asm__ __volatile__ (
800         "mov %%eax,%%esp;\n\t"  // Set stack pointer
801         "pop %%gs;\n\t"
802         "pop %%fs;\n\t"
803         "pop %%es;\n\t"
804         "pop %%ds;\n\t"
805         "popa;\n\t"
806         "iret;\n\t" : : "a" (stack));
807         for(;;);
808 }
809
810 /**
811  * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
812  * \brief Demotes a process to a lower permission level
813  * \param Err   Pointer to user's errno
814  * \param Dest  New Permission Level
815  * \param Regs  Pointer to user's register structure
816  */
817 int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
818 {
819          int    cpl = Regs->cs & 3;
820         // Sanity Check
821         if(Dest > 3 || Dest < 0) {
822                 *Err = -EINVAL;
823                 return -1;
824         }
825         
826         // Permission Check
827         if(cpl > Dest) {
828                 *Err = -EACCES;
829                 return -1;
830         }
831         
832         // Change the Segment Registers
833         Regs->cs = (((Dest+1)<<4) | Dest) - 8;
834         Regs->ss = ((Dest+1)<<4) | Dest;
835         // Check if the GP Segs are GDT, then change them
836         if(!(Regs->ds & 4))     Regs->ds = ((Dest+1)<<4) | Dest;
837         if(!(Regs->es & 4))     Regs->es = ((Dest+1)<<4) | Dest;
838         if(!(Regs->fs & 4))     Regs->fs = ((Dest+1)<<4) | Dest;
839         if(!(Regs->gs & 4))     Regs->gs = ((Dest+1)<<4) | Dest;
840         
841         return 0;
842 }
843
844 /**
845  * \brief Calls a signal handler in user mode
846  * \note Used for signals
847  */
848 void Proc_CallFaultHandler(tThread *Thread)
849 {
850         // Rewinds the stack and calls the user function
851         // Never returns
852         Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack );
853         for(;;);
854 }
855
856 void Proc_DumpThreadCPUState(tThread *Thread)
857 {
858         Uint32  *stack = (void *)Thread->SavedState.EBP;        // EBP = ESP after call and PUSH
859         
860         if( Thread->CurCPU > -1 )
861         {
862                 Log("  Currently running");
863                 return ;
864         }
865         
866         #if 1
867         tVAddr  diffFromScheduler = Thread->SavedState.EIP - (tVAddr)Proc_Scheduler;
868         tVAddr  diffFromClone = Thread->SavedState.EIP - (tVAddr)Proc_Clone;
869         tVAddr  diffFromSpawn = Thread->SavedState.EIP - (tVAddr)Proc_SpawnWorker;
870         
871         if( diffFromClone > 0 && diffFromClone < 512 )  // When I last checked, GetEIP was at .+0x183
872         {
873                 // Just spawned full thread
874                 Log("  Creating full thread");
875                 return ;
876         }
877         
878         if( diffFromSpawn > 0 && diffFromSpawn < 512 )  // When I last checked, GetEIP was at .+0x99
879         {
880                 // Just spawned worker thread
881                 Log("  Creating worker thread");
882                 return ;
883         }
884         
885         if( diffFromScheduler > 0 && diffFromScheduler < 256 )  // When I last checked, GetEIP was at .+0x60
886         #else
887         if( stack[1] == (Uint32)&IRQCommon + 25 )
888         {
889                 tRegs   *regs = (void *) stack[2];
890                 Log("  oldebp = 0x%08x, ret = 0x%08x, regs = 0x%x",
891                         stack[0], stack[1], stack[2]
892                         );
893                 // [EBP] = old EBP
894                 // [EBP+0x04] = Return Addr
895                 // [EBP+0x08] = Arg 1 (CPU Number)
896                 // [EBP+0x0C] = Arg 2 (Thread)
897                 // [EBP+0x10] = GS (start of tRegs)
898                 Log("  IRQ%i from %02x:%08x", regs->int_num regs->cs, regs->eip);
899         }
900         if( stack[1] == (Uint32)&scheduler_return )
901         #endif
902         {
903                 // Scheduled out
904                 tRegs   *regs = (void *) &stack[4];
905                 Log("  oldebp = 0x%08x, ret = 0x%08x, cpu = %i, thread = 0x%x",
906                         stack[0], stack[1], stack[2], stack[3]);
907                 // [EBP] = old EBP
908                 // [EBP+0x04] = Return Addr
909                 // [EBP+0x08] = Arg 1 (CPU Number)
910                 // [EBP+0x0C] = Arg 2 (Thread)
911                 // [EBP+0x10] = GS (start of tRegs)
912                 Log("  At %02x:%08x", regs->cs, regs->eip);
913                 return ;
914         }
915         
916         Log("  Just created");
917 }
918
919 /**
920  * \fn void Proc_Scheduler(int CPU)
921  * \brief Swap current thread and clears dead threads
922  */
923 void Proc_Scheduler(int CPU)
924 {
925         Uint    esp, ebp, eip;
926         tThread *thread;
927         
928         // If the spinlock is set, let it complete
929         if(IS_LOCKED(&glThreadListLock))        return;
930         
931         // Get current thread
932         #if USE_MP
933         thread = gaCPUs[CPU].Current;
934         #else
935         thread = gCurrentThread;
936         #endif
937         
938         if( thread )
939         {
940                 // Reduce remaining quantum and continue timeslice if non-zero
941                 if( thread->Remaining-- )
942                         return;
943                 // Reset quantum for next call
944                 thread->Remaining = thread->Quantum;
945                 
946                 // Get machine state
947                 __asm__ __volatile__ ( "mov %%esp, %0" : "=r" (esp) );
948                 __asm__ __volatile__ ( "mov %%ebp, %0" : "=r" (ebp) );
949                 eip = GetEIP();
950                 if(eip == SWITCH_MAGIC) return; // Check if a switch happened
951                 
952                 // Save machine state
953                 thread->SavedState.ESP = esp;
954                 thread->SavedState.EBP = ebp;
955                 thread->SavedState.EIP = eip;
956         }
957         
958         // Get next thread to run
959         thread = Threads_GetNextToRun(CPU, thread);
960         
961         // No avaliable tasks, just go into low power mode (idle thread)
962         if(thread == NULL) {
963                 #if USE_MP
964                 thread = gaCPUs[CPU].IdleThread;
965                 Log("CPU %i Running Idle Thread", CPU);
966                 #else
967                 thread = gpIdleThread;
968                 #endif
969         }
970         
971         // Set current thread
972         #if USE_MP
973         gaCPUs[CPU].Current = thread;
974         #else
975         gCurrentThread = thread;
976         #endif
977         
978         #if DEBUG_TRACE_SWITCH
979         Log("Switching to task %i, CR3 = 0x%x, EIP = %p",
980                 thread->TID,
981                 thread->MemState.CR3,
982                 thread->SavedState.EIP
983                 );
984         #endif
985         
986         #if USE_MP      // MP Debug
987 //      Log("CPU = %i, Thread %p", CPU, thread);
988         #endif
989         
990         // Update Kernel Stack pointer
991         gTSSs[CPU].ESP0 = thread->KernelStack-4;
992         
993         #if 0
994         if(thread->SavedState.ESP > 0xC0000000
995         && thread->SavedState.ESP < thread->KernelStack-0x2000) {
996                 Log_Warning("Proc", "Possible bad ESP %p (PID %i)", thread->SavedState.ESP);
997         }
998         #endif
999         
1000         #if USE_PAE
1001         # error "Todo: Implement PAE Address space switching"
1002         #else
1003         // Switch threads
1004         __asm__ __volatile__ (
1005                 "mov %4, %%cr3\n\t"     // Set address space
1006                 "mov %1, %%esp\n\t"     // Restore ESP
1007                 "mov %2, %%ebp\n\t"     // and EBP
1008                 "jmp *%3" : :   // And return to where we saved state (Proc_Clone or Proc_Scheduler)
1009                 "a"(SWITCH_MAGIC), "b"(thread->SavedState.ESP),
1010                 "d"(thread->SavedState.EBP), "c"(thread->SavedState.EIP),
1011                 "r"(thread->MemState.CR3)
1012                 );
1013         #endif
1014         for(;;);        // Shouldn't reach here
1015 }
1016
1017 // === EXPORTS ===
1018 EXPORT(Proc_SpawnWorker);

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