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 tSpinlock 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 gaCPUs[0].Current = &gThreadZero;
294 gThreadZero.MemState.CR3 = (Uint)gInitialPML4 - KERNEL_BASE;
296 // Set timer frequency
297 outb(0x43, 0x34); // Set Channel 0, Low/High, Rate Generator
298 outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
299 outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte
301 // Create Per-Process Data Block
302 MM_Allocate(MM_PPD_CFG);
309 void MP_StartAP(int CPU)
311 Log("Starting AP %i (APIC %i)", CPU, gaCPUs[CPU].APICID);
312 // Set location of AP startup code and mark for a warm restart
313 *(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APStartup - (KERNEL_BASE|0xFFFF0);
314 *(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
315 outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset
316 MP_SendIPI(gaCPUs[CPU].APICID, 0, 5);
320 void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode)
322 Uint32 addr = (Uint)gpMP_LocalAPIC + 0x300;
326 val = (Uint)APICID << 24;
327 Log("*%p = 0x%08x", addr+0x10, val);
328 *(Uint32*)(addr+0x10) = val;
330 val = ((DeliveryMode & 7) << 8) | (Vector & 0xFF);
331 Log("*%p = 0x%08x", addr, val);
332 *(Uint32*)addr = val;
337 * \fn void Proc_Start(void)
338 * \brief Start process scheduler
340 void Proc_Start(void)
348 for( i = 0; i < giNumCPUs; i ++ )
351 if(i) gaCPUs[i].Current = NULL;
354 if( (tid = Proc_Clone(0, 0)) == 0)
356 for(;;) HALT(); // Just yeilds
358 gaCPUs[i].IdleThread = Threads_GetThread(tid);
359 gaCPUs[i].IdleThread->ThreadName = "Idle Thread";
360 Threads_SetTickets( gaCPUs[i].IdleThread, 0 ); // Never called randomly
361 gaCPUs[i].IdleThread->Quantum = 1; // 1 slice quantum
365 if( i != giProc_BootProcessorID ) {
370 // BSP still should run the current task
371 gaCPUs[0].Current = &gThreadZero;
373 // Start interrupts and wait for APs to come up
374 Log("Waiting for APs to come up\n");
375 __asm__ __volatile__ ("sti");
376 while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
379 if(Proc_Clone(0, 0) == 0)
381 gaCPUs[0].IdleThread = Proc_GetCurThread();
382 gaCPUs[0].IdleThread->ThreadName = "Idle Thread";
383 gaCPUs[0].IdleThread->NumTickets = 0; // Never called randomly
384 gaCPUs[0].IdleThread->Quantum = 1; // 1 slice quantum
385 for(;;) HALT(); // Just yeilds
389 gaCPUs[0].Current = &gThreadZero;
391 // Start Interrupts (and hence scheduler)
392 __asm__ __volatile__("sti");
394 MM_FinishVirtualInit();
398 * \fn tThread *Proc_GetCurThread(void)
399 * \brief Gets the current thread
401 tThread *Proc_GetCurThread(void)
404 return gaCPUs[ GetCPUNum() ].Current;
406 return gaCPUs[ 0 ].Current;
411 * \fn void Proc_ChangeStack(void)
412 * \brief Swaps the current stack for a new one (in the proper stack reigon)
414 void Proc_ChangeStack(void)
417 Uint tmp_rbp, old_rsp;
418 Uint curBase, newBase;
420 __asm__ __volatile__ ("mov %%rsp, %0":"=r"(rsp));
421 __asm__ __volatile__ ("mov %%rbp, %0":"=r"(rbp));
426 newBase = MM_NewKStack();
429 Panic("What the?? Unable to allocate space for initial kernel stack");
433 curBase = (Uint)&gInitialKernelStack;
435 Log("curBase = 0x%x, newBase = 0x%x", curBase, newBase);
437 // Get ESP as a used size
439 Log("memcpy( %p, %p, 0x%x )", (void*)(newBase - rsp), (void*)(curBase - rsp), rsp );
441 memcpy( (void*)(newBase - rsp), (void*)(curBase - rsp), rsp );
442 // Get ESP as an offset in the new stack
445 rbp = newBase - (curBase - rbp);
448 // Repair EBPs & Stack Addresses
449 // Catches arguments also, but may trash stack-address-like values
450 for(tmp_rbp = rsp; tmp_rbp < newBase; tmp_rbp += sizeof(Uint))
452 if(old_rsp < *(Uint*)tmp_rbp && *(Uint*)tmp_rbp < curBase)
453 *(Uint*)tmp_rbp += newBase - curBase;
456 Log("Applying Changes");
457 Proc_GetCurThread()->KernelStack = newBase;
458 __asm__ __volatile__ ("mov %0, %%rsp"::"r"(rsp));
459 __asm__ __volatile__ ("mov %0, %%rbp"::"r"(rbp));
463 * \fn int Proc_Clone(Uint *Err, Uint Flags)
464 * \brief Clone the current process
466 int Proc_Clone(Uint *Err, Uint Flags)
469 tThread *cur = Proc_GetCurThread();
472 __asm__ __volatile__ ("mov %%rsp, %0": "=r"(rsp));
473 __asm__ __volatile__ ("mov %%rbp, %0": "=r"(rbp));
475 newThread = Threads_CloneTCB(Err, Flags);
476 if(!newThread) return -1;
478 Log("Proc_Clone: newThread = %p", newThread);
480 // Initialise Memory Space (New Addr space or kernel stack)
481 if(Flags & CLONE_VM) {
482 Log("Proc_Clone: Cloning VM");
483 newThread->MemState.CR3 = MM_Clone();
484 newThread->KernelStack = cur->KernelStack;
486 Uint tmp_rbp, old_rsp = rsp;
489 newThread->MemState.CR3 = cur->MemState.CR3;
492 newThread->KernelStack = MM_NewKStack();
493 Log("Proc_Clone: newKStack = %p", newThread->KernelStack);
495 if(newThread->KernelStack == 0) {
500 // Get ESP as a used size
501 rsp = cur->KernelStack - rsp;
504 (void*)(newThread->KernelStack - rsp),
505 (void*)(cur->KernelStack - rsp),
508 // Get ESP as an offset in the new stack
509 rsp = newThread->KernelStack - rsp;
511 rbp = newThread->KernelStack - (cur->KernelStack - rbp);
513 // Repair EBPs & Stack Addresses
514 // Catches arguments also, but may trash stack-address-like values
515 for(tmp_rbp = rsp; tmp_rbp < newThread->KernelStack; tmp_rbp += sizeof(Uint))
517 if(old_rsp < *(Uint*)tmp_rbp && *(Uint*)tmp_rbp < cur->KernelStack)
518 *(Uint*)tmp_rbp += newThread->KernelStack - cur->KernelStack;
522 // Save core machine state
523 newThread->SavedState.RSP = rsp;
524 newThread->SavedState.RBP = rbp;
526 if(rip == SWITCH_MAGIC) {
527 outb(0x20, 0x20); // ACK Timer and return as child
532 newThread->SavedState.RIP = rip;
534 // Lock list and add to active
535 Threads_AddActive(newThread);
537 return newThread->TID;
541 * \fn int Proc_SpawnWorker(void)
542 * \brief Spawns a new worker thread
544 int Proc_SpawnWorker(void)
549 cur = Proc_GetCurThread();
552 new = malloc( sizeof(tThread) );
554 Warning("Proc_SpawnWorker - Out of heap space!\n");
557 memcpy(new, &gThreadZero, sizeof(tThread));
559 new->TID = giNextTID++;
560 // Create a new worker stack (in PID0's address space)
561 // The stack is relocated by this code
562 new->KernelStack = MM_NewWorkerStack();
564 // Get ESP and EBP based in the new stack
565 __asm__ __volatile__ ("mov %%rsp, %0": "=r"(rsp));
566 __asm__ __volatile__ ("mov %%rbp, %0": "=r"(rbp));
567 rsp = new->KernelStack - (cur->KernelStack - rsp);
568 rbp = new->KernelStack - (cur->KernelStack - rbp);
570 // Save core machine state
571 new->SavedState.RSP = rsp;
572 new->SavedState.RBP = rbp;
574 if(rip == SWITCH_MAGIC) {
575 outb(0x20, 0x20); // ACK Timer and return as child
580 new->SavedState.RIP = rip;
582 new->Status = THREAD_STAT_ACTIVE;
583 Threads_AddActive( new );
589 * \fn Uint Proc_MakeUserStack(void)
590 * \brief Creates a new user stack
592 Uint Proc_MakeUserStack(void)
595 Uint base = USER_STACK_TOP - USER_STACK_SZ;
597 // Check Prospective Space
598 for( i = USER_STACK_SZ >> 12; i--; )
599 if( MM_GetPhysAddr( base + (i<<12) ) != 0 )
602 if(i != -1) return 0;
604 // Allocate Stack - Allocate incrementally to clean up MM_Dump output
605 for( i = 0; i < USER_STACK_SZ/4069; i++ )
606 MM_Allocate( base + (i<<12) );
608 return base + USER_STACK_SZ;
613 * \fn void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
614 * \brief Starts a user task
616 void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize)
618 Uint *stack = (void*)Proc_MakeUserStack();
623 LOG("stack = 0x%x", stack);
626 stack = (void*)( (Uint)stack - DataSize );
627 memcpy( stack, ArgV, DataSize );
629 // Adjust Arguments and environment
630 delta = (Uint)stack - (Uint)ArgV;
631 ArgV = (char**)stack;
632 for( i = 0; ArgV[i]; i++ ) ArgV[i] += delta;
635 for( i = 0; EnvP[i]; i++ ) EnvP[i] += delta;
637 // User Mode Segments
638 ss = 0x23; cs = 0x1B;
641 *--stack = (Uint)EnvP;
642 *--stack = (Uint)ArgV;
643 *--stack = (Uint)ArgC;
646 *--stack = 0; // Return Address
648 Proc_StartProcess(ss, (Uint)stack, 0x202, cs, Entrypoint);
651 void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP)
653 Uint *stack = (void*)Stack;
654 *--stack = SS; //Stack Segment
655 *--stack = Stack; //Stack Pointer
656 *--stack = Flags; //EFLAGS (Resvd (0x2) and IF (0x20))
657 *--stack = CS; //Code Segment
660 *--stack = 0xAAAAAAAA; // eax
661 *--stack = 0xCCCCCCCC; // ecx
662 *--stack = 0xDDDDDDDD; // edx
663 *--stack = 0xBBBBBBBB; // ebx
664 *--stack = 0xD1D1D1D1; // edi
665 *--stack = 0x54545454; // rsp - NOT POPED
666 *--stack = 0x51515151; // esi
667 *--stack = 0xB4B4B4B4; // rbp
671 __asm__ __volatile__ (
672 "mov %%rax,%%rsp;\n\t" // Set stack pointer
673 "iret;\n\t" : : "a" (stack));
678 * \fn int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
679 * \brief Demotes a process to a lower permission level
680 * \param Err Pointer to user's errno
681 * \param Dest New Permission Level
682 * \param Regs Pointer to user's register structure
684 int Proc_Demote(Uint *Err, int Dest, tRegs *Regs)
686 int cpl = Regs->CS & 3;
688 if(Dest > 3 || Dest < 0) {
699 // Change the Segment Registers
700 Regs->CS = (((Dest+1)<<4) | Dest) - 8;
701 Regs->SS = ((Dest+1)<<4) | Dest;
707 * \brief Calls a signal handler in user mode
708 * \note Used for signals
710 void Proc_CallFaultHandler(tThread *Thread)
712 // Rewinds the stack and calls the user function
714 __asm__ __volatile__ ("mov %0, %%rbp;\n\tcall Proc_ReturnToUser" :: "r"(Thread->FaultHandler));
719 * \fn void Proc_Scheduler(int CPU)
720 * \brief Swap current thread and clears dead threads
722 void Proc_Scheduler(int CPU)
727 // If the spinlock is set, let it complete
728 if(IS_LOCKED(&glThreadListLock)) return;
730 // Get current thread
731 thread = gaCPUs[CPU].Current;
733 // Reduce remaining quantum and continue timeslice if non-zero
734 if(thread->Remaining--) return;
735 // Reset quantum for next call
736 thread->Remaining = thread->Quantum;
739 __asm__ __volatile__ ("mov %%rsp, %0":"=r"(rsp));
740 __asm__ __volatile__ ("mov %%rbp, %0":"=r"(rbp));
742 if(rip == SWITCH_MAGIC) return; // Check if a switch happened
744 // Save machine state
745 thread->SavedState.RSP = rsp;
746 thread->SavedState.RBP = rbp;
747 thread->SavedState.RIP = rip;
750 thread = Threads_GetNextToRun(CPU, thread);
754 thread = gaCPUs[CPU].IdleThread;
755 Warning("Hmm... Threads_GetNextToRun returned NULL, I don't think this should happen.\n");
759 #if DEBUG_TRACE_SWITCH
760 Log("Switching to task %i, CR3 = 0x%x, RIP = %p",
762 thread->MemState.CR3,
763 thread->SavedState.RIP
767 // Set current thread
768 gaCPUs[CPU].Current = thread;
770 // Update Kernel Stack pointer
771 gTSSs[CPU].RSP0 = thread->KernelStack-4;
774 __asm__ __volatile__ ("mov %0, %%cr3"::"a"(thread->MemState.CR3));
777 __asm__ __volatile__ (
778 "mov %1, %%rsp\n\t" // Restore RSP
779 "mov %2, %%rbp\n\t" // and RBP
780 "jmp *%3" : : // And return to where we saved state (Proc_Clone or Proc_Scheduler)
781 "a"(SWITCH_MAGIC), "b"(thread->SavedState.RSP),
782 "d"(thread->SavedState.RBP), "c"(thread->SavedState.RIP)
784 for(;;); // Shouldn't reach here
788 EXPORT(Proc_SpawnWorker);