16 #define DEBUG_TRACE_SWITCH 0
19 #define SWITCH_MAGIC 0x55ECAFFF##FFFACE55 // There is no code in this area
21 #define TIMER_DIVISOR 11931 //~100Hz
27 Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
35 extern void APStartup(void); // 16-bit AP startup code
36 extern Uint GetRIP(void); // start.asm
37 extern Uint64 gInitialPML4[512]; // start.asm
38 extern char gInitialKernelStack[];
39 extern tShortSpinlock glThreadListLock;
42 extern int giTotalTickets;
43 extern int giNumActiveThreads;
44 extern tThread gThreadZero;
45 //extern tThread *Threads_GetNextToRun(int CPU);
46 extern void Threads_Dump(void);
47 extern tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
48 extern void Proc_ReturnToUser(void);
49 extern int GetCPUNum(void);
52 void ArchThreads_Init(void);
54 void MP_StartAP(int CPU);
55 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
57 void Proc_Start(void);
58 tThread *Proc_GetCurThread(void);
59 void Proc_ChangeStack(void);
60 int Proc_Clone(Uint *Err, Uint Flags);
61 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP);
62 void Proc_CallFaultHandler(tThread *Thread);
63 void Proc_Scheduler(int CPU);
66 // --- Multiprocessing ---
68 volatile int giNumInitingCPUs = 0;
69 tMPInfo *gMPFloatPtr = NULL;
70 tAPIC *gpMP_LocalAPIC = NULL;
71 Uint8 gaAPIC_to_CPU[256] = {0};
73 tCPU gaCPUs[MAX_CPUS];
76 // --- Error Recovery ---
77 Uint32 gaDoubleFaultStack[1024];
81 * \fn void ArchThreads_Init(void)
82 * \brief Starts the process scheduler
84 void ArchThreads_Init(void)
94 // -- Initialise Multiprocessing
95 // Find MP Floating Table
96 // - EBDA/Last 1Kib (640KiB)
97 for(pos = KERNEL_BASE|0x9F000; pos < (KERNEL_BASE|0xA0000); pos += 16) {
98 if( *(Uint*)(pos) == MPPTR_IDENT ) {
99 Log("Possible %p", pos);
100 if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
101 gMPFloatPtr = (void*)pos;
105 // - Last KiB (512KiB base mem)
107 for(pos = KERNEL_BASE|0x7F000; pos < (KERNEL_BASE|0x80000); pos += 16) {
108 if( *(Uint*)(pos) == MPPTR_IDENT ) {
109 Log("Possible %p", pos);
110 if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
111 gMPFloatPtr = (void*)pos;
118 for(pos = KERNEL_BASE|0xE0000; pos < (KERNEL_BASE|0x100000); pos += 16) {
119 if( *(Uint*)(pos) == MPPTR_IDENT ) {
120 Log("Possible %p", pos);
121 if( ByteSum((void*)pos, sizeof(tMPInfo)) != 0 ) continue;
122 gMPFloatPtr = (void*)pos;
128 // If the MP Table Exists, parse it
133 Log("gMPFloatPtr = %p", gMPFloatPtr);
134 Log("*gMPFloatPtr = {");
135 Log("\t.Sig = 0x%08x", gMPFloatPtr->Sig);
136 Log("\t.MPConfig = 0x%08x", gMPFloatPtr->MPConfig);
137 Log("\t.Length = 0x%02x", gMPFloatPtr->Length);
138 Log("\t.Version = 0x%02x", gMPFloatPtr->Version);
139 Log("\t.Checksum = 0x%02x", gMPFloatPtr->Checksum);
140 Log("\t.Features = [0x%02x,0x%02x,0x%02x,0x%02x,0x%02x]",
141 gMPFloatPtr->Features[0], gMPFloatPtr->Features[1],
142 gMPFloatPtr->Features[2], gMPFloatPtr->Features[3],
143 gMPFloatPtr->Features[4]
147 mptable = (void*)( KERNEL_BASE|gMPFloatPtr->MPConfig );
148 Log("mptable = %p", mptable);
150 Log("\t.Sig = 0x%08x", mptable->Sig);
151 Log("\t.BaseTableLength = 0x%04x", mptable->BaseTableLength);
152 Log("\t.SpecRev = 0x%02x", mptable->SpecRev);
153 Log("\t.Checksum = 0x%02x", mptable->Checksum);
154 Log("\t.OEMID = '%8c'", mptable->OemID);
155 Log("\t.ProductID = '%8c'", mptable->ProductID);
156 Log("\t.OEMTablePtr = %p'", mptable->OEMTablePtr);
157 Log("\t.OEMTableSize = 0x%04x", mptable->OEMTableSize);
158 Log("\t.EntryCount = 0x%04x", mptable->EntryCount);
159 Log("\t.LocalAPICMemMap = 0x%08x", mptable->LocalAPICMemMap);
160 Log("\t.ExtendedTableLen = 0x%04x", mptable->ExtendedTableLen);
161 Log("\t.ExtendedTableChecksum = 0x%02x", mptable->ExtendedTableChecksum);
164 gpMP_LocalAPIC = (void*)MM_MapHWPage(mptable->LocalAPICMemMap, 1);
166 ents = mptable->Entries;
169 for( i = 0; i < mptable->EntryCount; i ++ )
176 Log("%i: Processor", i);
177 Log("\t.APICID = %i", ents->Proc.APICID);
178 Log("\t.APICVer = 0x%02x", ents->Proc.APICVer);
179 Log("\t.CPUFlags = 0x%02x", ents->Proc.CPUFlags);
180 Log("\t.CPUSignature = 0x%08x", ents->Proc.CPUSignature);
181 Log("\t.FeatureFlags = 0x%08x", ents->Proc.FeatureFlags);
184 if( !(ents->Proc.CPUFlags & 1) ) {
189 // Check if there is too many processors
190 if(giNumCPUs >= MAX_CPUS) {
191 giNumCPUs ++; // If `giNumCPUs` > MAX_CPUS later, it will be clipped
195 // Initialise CPU Info
196 gaAPIC_to_CPU[ents->Proc.APICID] = giNumCPUs;
197 gaCPUs[giNumCPUs].APICID = ents->Proc.APICID;
198 gaCPUs[giNumCPUs].State = 0;
202 if( !(ents->Proc.CPUFlags & 2) )
204 MP_StartAP( giNumCPUs-1 );
211 Log("\t.ID = %i", ents->Bus.ID);
212 Log("\t.TypeString = '%6c'", ents->Bus.TypeString);
216 Log("%i: I/O APIC", i);
217 Log("\t.ID = %i", ents->IOAPIC.ID);
218 Log("\t.Version = 0x%02x", ents->IOAPIC.Version);
219 Log("\t.Flags = 0x%02x", ents->IOAPIC.Flags);
220 Log("\t.Addr = 0x%08x", ents->IOAPIC.Addr);
222 case 3: // I/O Interrupt Assignment
224 Log("%i: I/O Interrupt Assignment", i);
225 Log("\t.IntType = %i", ents->IOInt.IntType);
226 Log("\t.Flags = 0x%04x", ents->IOInt.Flags);
227 Log("\t.SourceBusID = 0x%02x", ents->IOInt.SourceBusID);
228 Log("\t.SourceBusIRQ = 0x%02x", ents->IOInt.SourceBusIRQ);
229 Log("\t.DestAPICID = 0x%02x", ents->IOInt.DestAPICID);
230 Log("\t.DestAPICIRQ = 0x%02x", ents->IOInt.DestAPICIRQ);
232 case 4: // Local Interrupt Assignment
234 Log("%i: Local Interrupt Assignment", i);
235 Log("\t.IntType = %i", ents->LocalInt.IntType);
236 Log("\t.Flags = 0x%04x", ents->LocalInt.Flags);
237 Log("\t.SourceBusID = 0x%02x", ents->LocalInt.SourceBusID);
238 Log("\t.SourceBusIRQ = 0x%02x", ents->LocalInt.SourceBusIRQ);
239 Log("\t.DestLocalAPICID = 0x%02x", ents->LocalInt.DestLocalAPICID);
240 Log("\t.DestLocalAPICIRQ = 0x%02x", ents->LocalInt.DestLocalAPICIRQ);
243 Log("%i: Unknown (%i)", i, ents->Type);
246 ents = (void*)( (Uint)ents + entSize );
249 if( giNumCPUs > MAX_CPUS ) {
250 Warning("Too many CPUs detected (%i), only using %i of them", giNumCPUs, MAX_CPUS);
251 giNumCPUs = MAX_CPUS;
254 while( giNumInitingCPUs )
255 MM_FinishVirtualInit();
257 Panic("Uh oh... MP Table Parsing is unimplemented\n");
260 Log("No MP Table was found, assuming uniprocessor\n");
267 MM_FinishVirtualInit();
271 // Initialise Normal TSS(s)
272 for(pos=0;pos<giNumCPUs;pos++)
277 gTSSs[pos].RSP0 = 0; // Set properly by scheduler
278 gGDT[7+pos*2].LimitLow = sizeof(tTSS) & 0xFFFF;
279 gGDT[7+pos*2].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
280 gGDT[7+pos*2].BaseMid = ((Uint)(&gTSSs[pos])) >> 16;
281 gGDT[7+pos*2].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
282 gGDT[7+pos*2+1].DWord[0] = ((Uint)(&gTSSs[pos])) >> 32;
285 for(pos=0;pos<giNumCPUs;pos++) {
287 __asm__ __volatile__ ("ltr %%ax"::"a"(0x38+pos*16));
292 // Set Debug registers
293 __asm__ __volatile__ ("mov %0, %%db0" : : "r"(&gThreadZero));
294 __asm__ __volatile__ ("mov %%rax, %%db1" : : "a"(0));
296 gaCPUs[0].Current = &gThreadZero;
298 gThreadZero.MemState.CR3 = (Uint)gInitialPML4 - KERNEL_BASE;
299 gThreadZero.CurCPU = 0;
301 // Set timer frequency
302 outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
303 outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
304 outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte
306 // Create Per-Process Data Block
307 MM_Allocate(MM_PPD_CFG);
312 Log("Multithreading initialised");
316 void MP_StartAP(int CPU)
318 Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
319 // Set location of AP startup code and mark for a warm restart
320 *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);
321 *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
322 outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset
323 MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);
327 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
329 Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300;
333 val = (Uint)APICID << 24;
334 Log("*%p = 0x%08x", addr+0x10, val);
335 *(Uint32*)(addr+0x10) = val;
337 val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
338 Log("*%p = 0x%08x", addr, val);
339 *(Uint32*)addr = val;
344 * \fn void Proc_Start(void)
345 * \brief Start process scheduler
347 void Proc_Start(void)
355 for( i = 0; i < giNumCPUs; i ++ )
358 if(i) gaCPUs[i].Current = NULL;
361 if( (tid = Proc_Clone(0, 0)) == 0)
363 for(;;) HALT(); // Just yeilds
365 gaCPUs[i].IdleThread = Threads_GetThread(tid);
366 gaCPUs[i].IdleThread->ThreadName = "Idle Thread";
367 Threads_SetTickets( gaCPUs[i].IdleThread, 0 ); // Never called randomly
368 gaCPUs[i].IdleThread->Quantum = 1; // 1 slice quantum
372 if( i != giProc_BootProcessorID ) {
377 // BSP still should run the current task
378 gaCPUs[0].Current = &gThreadZero;
380 // Start interrupts and wait for APs to come up
381 Log("Waiting for APs to come up\n");
382 __asm__ __volatile__ ("sti");
383 while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
386 if(Proc_Clone(0, 0) == 0)
388 gaCPUs[0].IdleThread = Proc_GetCurThread();
389 gaCPUs[0].IdleThread->ThreadName = "Idle Thread";
390 gaCPUs[0].IdleThread->NumTickets = 0; // Never called randomly
391 gaCPUs[0].IdleThread->Quantum = 1; // 1 slice quantum
392 for(;;) HALT(); // Just yeilds
396 gaCPUs[0].Current = &gThreadZero;
397 gaCPUs[0].Current->CurCPU = 0;
399 // Start Interrupts (and hence scheduler)
400 __asm__ __volatile__("sti");
402 MM_FinishVirtualInit();
403 Log("Multithreading started");
407 * \fn tThread *Proc_GetCurThread(void)
408 * \brief Gets the current thread
410 tThread *Proc_GetCurThread(void)
413 return gaCPUs[ GetCPUNum() ].Current;
415 return gaCPUs[ 0 ].Current;
420 * \fn void Proc_ChangeStack(void)
421 * \brief Swaps the current stack for a new one (in the proper stack reigon)
423 void Proc_ChangeStack(void)
426 Uint tmp_rbp, old_rsp;
427 Uint curBase, newBase;
429 __asm__ __volatile__ ("mov %%rsp, %0":"=r"(rsp));
430 __asm__ __volatile__ ("mov %%rbp, %0":"=r"(rbp));
435 newBase = MM_NewKStack();
438 Panic("What the?? Unable to allocate space for initial kernel stack");
442 curBase = (Uint)&gInitialKernelStack;
444 Log("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
446 // Get ESP as a used size
448 Log("memcpy( %p, %p, 0x%x )", (void*)(newBase - rsp), (void*)(curBase - rsp), rsp );
450 memcpy( (void*)(newBase - rsp), (void*)(curBase - rsp), rsp );
451 // Get ESP as an offset in the new stack
454 rbp = newBase - (curBase - rbp);
457 // Repair EBPs & Stack Addresses
458 // Catches arguments also, but may trash stack-address-like values
459 for(tmp_rbp = rsp; tmp_rbp < newBase; tmp_rbp += sizeof(Uint))
461 if(old_rsp < *(Uint*)tmp_rbp && *(Uint*)tmp_rbp < curBase)
462 *(Uint*)tmp_rbp += newBase - curBase;
465 Log("Applying Changes");
466 Proc_GetCurThread()->KernelStack = newBase;
467 __asm__ __volatile__ ("mov %0, %%rsp"::"r"(rsp));
468 __asm__ __volatile__ ("mov %0, %%rbp"::"r"(rbp));
472 * \fn int Proc_Clone(Uint *Err, Uint Flags)
473 * \brief Clone the current process
475 int Proc_Clone(Uint *Err, Uint Flags)
478 tThread *cur = Proc_GetCurThread();
481 __asm__ __volatile__ ("mov %%rsp, %0": "=r"(rsp));
482 __asm__ __volatile__ ("mov %%rbp, %0": "=r"(rbp));
484 newThread = Threads_CloneTCB(Err, Flags);
485 if(!newThread) return -1;
487 Log("Proc_Clone: newThread = %p", newThread);
489 // Initialise Memory Space (New Addr space or kernel stack)
490 if(Flags & CLONE_VM) {
491 Log("Proc_Clone: Cloning VM");
492 newThread->MemState.CR3 = MM_Clone();
493 newThread->KernelStack = cur->KernelStack;
495 Uint tmp_rbp, old_rsp = rsp;
498 newThread->MemState.CR3 = cur->MemState.CR3;
501 newThread->KernelStack = MM_NewKStack();
502 Log("Proc_Clone: newKStack = %p", newThread->KernelStack);
504 if(newThread->KernelStack == 0) {
509 // Get ESP as a used size
510 rsp = cur->KernelStack - rsp;
513 (void*)(newThread->KernelStack - rsp),
514 (void*)(cur->KernelStack - rsp),
517 // Get ESP as an offset in the new stack
518 rsp = newThread->KernelStack - rsp;
520 rbp = newThread->KernelStack - (cur->KernelStack - rbp);
522 // Repair EBPs & Stack Addresses
523 // Catches arguments also, but may trash stack-address-like values
524 for(tmp_rbp = rsp; tmp_rbp < newThread->KernelStack; tmp_rbp += sizeof(Uint))
526 if(old_rsp < *(Uint*)tmp_rbp && *(Uint*)tmp_rbp < cur->KernelStack)
527 *(Uint*)tmp_rbp += newThread->KernelStack - cur->KernelStack;
531 // Save core machine state
532 newThread->SavedState.RSP = rsp;
533 newThread->SavedState.RBP = rbp;
535 if(rip == SWITCH_MAGIC) {
536 outb(0x20, 0x20); // ACK Timer and return as child
541 newThread->SavedState.RIP = rip;
543 // Lock list and add to active
544 Threads_AddActive(newThread);
546 return newThread->TID;
550 * \fn int Proc_SpawnWorker(void)
551 * \brief Spawns a new worker thread
553 int Proc_SpawnWorker(void)
558 cur = Proc_GetCurThread();
561 new = malloc( sizeof(tThread) );
563 Warning("Proc_SpawnWorker - Out of heap space!\n");
566 memcpy(new, &gThreadZero, sizeof(tThread));
568 new->TID = giNextTID++;
569 // Create a new worker stack (in PID0's address space)
570 // The stack is relocated by this code
571 new->KernelStack = MM_NewWorkerStack();
573 // Get ESP and EBP based in the new stack
574 __asm__ __volatile__ ("mov %%rsp, %0": "=r"(rsp));
575 __asm__ __volatile__ ("mov %%rbp, %0": "=r"(rbp));
576 rsp = new->KernelStack - (cur->KernelStack - rsp);
577 rbp = new->KernelStack - (cur->KernelStack - rbp);
579 // Save core machine state
580 new->SavedState.RSP = rsp;
581 new->SavedState.RBP = rbp;
583 if(rip == SWITCH_MAGIC) {
584 outb(0x20, 0x20); // ACK Timer and return as child
589 new->SavedState.RIP = rip;
591 new->Status = THREAD_STAT_ACTIVE;
592 Threads_AddActive( new );
598 * \fn Uint Proc_MakeUserStack(void)
599 * \brief Creates a new user stack
601 Uint Proc_MakeUserStack(void)
604 Uint base = USER_STACK_TOP - USER_STACK_SZ;
606 // Check Prospective Space
607 for( i = USER_STACK_SZ >> 12; i--; )
608 if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
611 if(i != -1) return 0;
613 // Allocate Stack - Allocate incrementally to clean up MM_Dump output
614 for( i = 0; i < USER_STACK_SZ/4069; i++ )
615 MM_Allocate( base + (i<<12) );
617 return base + USER_STACK_SZ;
622 * \fn void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
623 * \brief Starts a user task
625 void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
627 Uint *stack = (void*)Proc_MakeUserStack();
632 LOG("stack = 0x%x", stack);
635 stack = (void*)( (Uint)stack - DataSize );
636 memcpy( stack, ArgV, DataSize );
638 // Adjust Arguments and environment
639 delta = (Uint)stack - (Uint)ArgV;
640 ArgV = (char**)stack;
641 for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
644 for( i = 0; EnvP[i]; i++ ) EnvP[i] += delta;
646 // User Mode Segments
647 ss = 0x23; cs = 0x1B;
650 *--stack = (Uint)EnvP;
651 *--stack = (Uint)ArgV;
652 *--stack = (Uint)ArgC;
655 *--stack = 0; // Return Address
657 Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
660 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
662 Uint *stack = (void*)Stack;
663 *--stack = SS; //Stack Segment
664 *--stack = Stack; //Stack Pointer
665 *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20))
666 *--stack = CS; //Code Segment
669 *--stack = 0xAAAAAAAA; // eax
670 *--stack = 0xCCCCCCCC; // ecx
671 *--stack = 0xDDDDDDDD; // edx
672 *--stack = 0xBBBBBBBB; // ebx
673 *--stack = 0xD1D1D1D1; // edi
674 *--stack = 0x54545454; // rsp - NOT POPED
675 *--stack = 0x51515151; // esi
676 *--stack = 0xB4B4B4B4; // rbp
680 __asm__ __volatile__ (
681 "mov %%rax,%%rsp;\n\t" // Set stack pointer
682 "iret;\n\t" : : "a" (stack));
687 * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
688 * \brief Demotes a process to a lower permission level
689 * \param Err Pointer to user's errno
690 * \param Dest New Permission Level
691 * \param Regs Pointer to user's register structure
693 int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
695 int cpl = Regs->CS & 3;
697 if(Dest > 3 || Dest < 0) {
708 // Change the Segment Registers
709 Regs->CS = (((Dest+1)<<4) | Dest) - 8;
710 Regs->SS = ((Dest+1)<<4) | Dest;
716 * \brief Calls a signal handler in user mode
717 * \note Used for signals
719 void Proc_CallFaultHandler(tThread *Thread)
721 // Rewinds the stack and calls the user function
723 __asm__ __volatile__ ("mov %0, %%rbp;\n\tcall Proc_ReturnToUser" :: "r"(Thread->FaultHandler));
728 * \fn void Proc_Scheduler(int CPU)
729 * \brief Swap current thread and clears dead threads
731 void Proc_Scheduler(int CPU)
736 // If the spinlock is set, let it complete
737 if(IS_LOCKED(&glThreadListLock)) return;
739 // Get current thread
740 thread = gaCPUs[CPU].Current;
742 // Reduce remaining quantum and continue timeslice if non-zero
743 if(thread->Remaining--) return;
744 // Reset quantum for next call
745 thread->Remaining = thread->Quantum;
748 __asm__ __volatile__ ("mov %%rsp, %0":"=r"(rsp));
749 __asm__ __volatile__ ("mov %%rbp, %0":"=r"(rbp));
751 if(rip == SWITCH_MAGIC) return; // Check if a switch happened
753 // Save machine state
754 thread->SavedState.RSP = rsp;
755 thread->SavedState.RBP = rbp;
756 thread->SavedState.RIP = rip;
759 thread = Threads_GetNextToRun(CPU, thread);
763 thread = gaCPUs[CPU].IdleThread;
764 Warning("Hmm... Threads_GetNextToRun returned NULL, I don't think this should happen.\n");
768 #if DEBUG_TRACE_SWITCH
769 Log("Switching to task %i, CR3 = 0x%x, RIP = %p",
771 thread->MemState.CR3,
772 thread->SavedState.RIP
776 // Set current thread
777 gaCPUs[CPU].Current = thread;
779 // Update Kernel Stack pointer
780 gTSSs[CPU].RSP0 = thread->KernelStack-4;
783 __asm__ __volatile__ ("mov %0, %%cr3"::"a"(thread->MemState.CR3));
786 __asm__ __volatile__ (
787 "mov %1, %%rsp\n\t" // Restore RSP
788 "mov %2, %%rbp\n\t" // and RBP
789 "jmp *%3" : : // And return to where we saved state (Proc_Clone or Proc_Scheduler)
790 "a"(SWITCH_MAGIC), "b"(thread->SavedState.RSP),
791 "d"(thread->SavedState.RBP), "c"(thread->SavedState.RIP)
793 for(;;); // Shouldn't reach here
797 EXPORT(Proc_SpawnWorker);