// === FLAGS ===
#define DEBUG_TRACE_SWITCH 0
+#define DEBUG_DISABLE_DOUBLEFAULT 1
// === CONSTANTS ===
-#define SWITCH_MAGIC 0xFFFACE55 // There is no code in this area
+#define SWITCH_MAGIC 0xFF5317C8 // FF SWITCH - There is no code in this area
// Base is 1193182
#define TIMER_BASE 1193182
-#define TIMER_DIVISOR 11931 //~100Hz
+#define TIMER_DIVISOR 11932 //~100Hz
// === TYPES ===
#if USE_MP
Uint8 State; // 0: Unavaliable, 1: Idle, 2: Active
Uint16 Resvd;
tThread *Current;
+ tThread *IdleThread;
} tCPU;
#endif
extern Uint GetEIP(void); // start.asm
extern int GetCPUNum(void); // start.asm
extern Uint32 gaInitPageDir[1024]; // start.asm
-extern void Kernel_Stack_Top;
-extern tSpinlock glThreadListLock;
+extern char Kernel_Stack_Top[];
+extern tShortSpinlock glThreadListLock;
extern int giNumCPUs;
extern int giNextTID;
-extern int giTotalTickets;
-extern int giNumActiveThreads;
extern tThread gThreadZero;
-extern tThread *gActiveThreads;
-extern tThread *gSleepingThreads;
-extern tThread *gDeleteThreads;
-extern void Threads_Dump(void);
-extern tThread *Threads_CloneTCB(Uint *Err, Uint Flags);
extern void Isr8(void); // Double Fault
-extern void Proc_ReturnToUser(void);
+extern void Proc_ReturnToUser(tVAddr Handler, Uint Argument, tVAddr KernelStack);
// === PROTOTYPES ===
void ArchThreads_Init(void);
void MP_StartAP(int CPU);
void MP_SendIPI(Uint8 APICID, int Vector, int DeliveryMode);
#endif
-void Proc_Start(void);
-tThread *Proc_GetCurThread(void);
+//void Proc_Start(void);
+//tThread *Proc_GetCurThread(void);
void Proc_ChangeStack(void);
- int Proc_Clone(Uint *Err, Uint Flags);
+// int Proc_Clone(Uint *Err, Uint Flags);
+Uint Proc_MakeUserStack(void);
+void Proc_StartUser(Uint Entrypoint, Uint *Bases, int ArgC, char **ArgV, char **EnvP, int DataSize);
void Proc_StartProcess(Uint16 SS, Uint Stack, Uint Flags, Uint16 CS, Uint IP);
+ int Proc_Demote(Uint *Err, int Dest, tRegs *Regs);
void Proc_CallFaultHandler(tThread *Thread);
void Proc_Scheduler(int CPU);
int giProc_BootProcessorID = 0;
#else
tThread *gCurrentThread = NULL;
+tThread *gpIdleThread = NULL;
#endif
#if USE_PAE
Uint32 *gPML4s[4] = NULL;
MM_FinishVirtualInit();
#endif
- #if 0
+ #if !DEBUG_DISABLE_DOUBLEFAULT
// Initialise Double Fault TSS
gGDT[5].BaseLow = (Uint)&gDoubleFault_TSS & 0xFFFF;
gGDT[5].BaseMid = (Uint)&gDoubleFault_TSS >> 16;
outb(0x40, TIMER_DIVISOR&0xFF); // Low Byte of Divisor
outb(0x40, (TIMER_DIVISOR>>8)&0xFF); // High Byte
+ Log("Timer Frequency %i.%03i Hz",
+ TIMER_BASE/TIMER_DIVISOR,
+ ((Uint64)TIMER_BASE*1000/TIMER_DIVISOR)%1000
+ );
+
#if USE_MP
// Get the count setting for APIC timer
Log("Determining APIC Count");
gTSSs[pos].SS0 = 0x10;
gTSSs[pos].ESP0 = 0; // Set properly by scheduler
gGDT[6+pos].BaseLow = ((Uint)(&gTSSs[pos])) & 0xFFFF;
- gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos])) >> 16;
+ gGDT[6+pos].BaseMid = ((Uint)(&gTSSs[pos]) >> 16) & 0xFFFF;
gGDT[6+pos].BaseHi = ((Uint)(&gTSSs[pos])) >> 24;
#if USE_MP
}
// Load the BSP's TSS
__asm__ __volatile__ ("ltr %%ax"::"a"(0x30));
+ // Set Current Thread and CPU Number in DR0 and DR1
+ __asm__ __volatile__ ("mov %0, %%db0"::"r"(&gThreadZero));
+ __asm__ __volatile__ ("mov %0, %%db1"::"r"(0));
#if USE_MP
gaCPUs[0].Current = &gThreadZero;
#endif
// Create Per-Process Data Block
- MM_Allocate(MM_PPD_CFG);
+ if( !MM_Allocate(MM_PPD_CFG) )
+ {
+ Panic("OOM - No space for initiali Per-Process Config");
+ }
// Change Stacks
Proc_ChangeStack();
// Set location of AP startup code and mark for a warm restart
*(Uint16*)(KERNEL_BASE|0x467) = (Uint)&APWait - (KERNEL_BASE|0xFFFF0);
*(Uint16*)(KERNEL_BASE|0x469) = 0xFFFF;
- outb(0x70, 0x0F); outb(0x71, 0x0A); // Warm Reset
+ outb(0x70, 0x0F); outb(0x71, 0x0A); // Set warm reset flag
MP_SendIPI(gaCPUs[CPU].APICID, 0, 5); // Init IPI
// Delay
int i;
#endif
- // Start Interrupts (and hence scheduler)
- __asm__ __volatile__("sti");
-
#if USE_MP
// Start APs
for( i = 0; i < giNumCPUs; i ++ )
{
- gaCPUs[i].Current = NULL;
+ int tid;
+ if(i) gaCPUs[i].Current = NULL;
+
+ // Create Idle Task
+ if( (tid = Proc_Clone(0, 0)) == 0)
+ {
+ for(;;) HALT(); // Just yeilds
+ }
+ gaCPUs[i].IdleThread = Threads_GetThread(tid);
+ gaCPUs[i].IdleThread->ThreadName = (char*)"Idle Thread";
+ Threads_SetPriority( gaCPUs[i].IdleThread, -1 ); // Never called randomly
+ gaCPUs[i].IdleThread->Quantum = 1; // 1 slice quantum
+
+
+ // Start the AP
if( i != giProc_BootProcessorID ) {
MP_StartAP( i );
}
}
+ // BSP still should run the current task
+ gaCPUs[0].Current = &gThreadZero;
+
+ // Start interrupts and wait for APs to come up
Log("Waiting for APs to come up\n");
- //__asm__ __volatile__ ("sti");
+ __asm__ __volatile__ ("sti");
while( giNumInitingCPUs ) __asm__ __volatile__ ("hlt");
- MM_FinishVirtualInit();
+ #else
+ // Create Idle Task
+ if(Proc_Clone(0, 0) == 0)
+ {
+ gpIdleThread = Proc_GetCurThread();
+ gpIdleThread->ThreadName = strdup("Idle Thread");
+ Threads_SetPriority( gpIdleThread, -1 ); // Never called randomly
+ gpIdleThread->Quantum = 1; // 1 slice quantum
+ for(;;) HALT(); // Just yeilds
+ }
+
+ // Set current task
+ gCurrentThread = &gThreadZero;
+
+ // Start Interrupts (and hence scheduler)
+ __asm__ __volatile__("sti");
#endif
+ MM_FinishVirtualInit();
}
/**
tThread *Proc_GetCurThread(void)
{
#if USE_MP
- //return gaCPUs[ gaAPIC_to_CPU[gpMP_LocalAPIC->ID.Val&0xFF] ].Current;
return gaCPUs[ GetCPUNum() ].Current;
#else
return gCurrentThread;
// Initialise Memory Space (New Addr space or kernel stack)
if(Flags & CLONE_VM) {
newThread->MemState.CR3 = MM_Clone();
+ // Check for errors
+ if(newThread->MemState.CR3 == 0) {
+ Threads_Kill(newThread, -2);
+ return -1;
+ }
newThread->KernelStack = cur->KernelStack;
} else {
Uint tmpEbp, oldEsp = esp;
// Set CR3
+ #if USE_PAE
+ # warning "PAE Unimplemented"
+ #else
newThread->MemState.CR3 = cur->MemState.CR3;
+ #endif
// Create new KStack
newThread->KernelStack = MM_NewKStack();
// Check for errors
if(newThread->KernelStack == 0) {
- free(newThread);
+ Threads_Kill(newThread, -2);
return -1;
}
newThread->SavedState.EBP = ebp;
eip = GetEIP();
if(eip == SWITCH_MAGIC) {
- outb(0x20, 0x20); // ACK Timer and return as child
+ __asm__ __volatile__ ("mov %0, %%db0" : : "r" (newThread) );
+ #if USE_MP
+ // ACK the interrupt
+ if( GetCPUNum() )
+ gpMP_LocalAPIC->EOI.Val = 0;
+ else
+ #endif
+ outb(0x20, 0x20); // ACK Timer and return as child
__asm__ __volatile__ ("sti"); // Restart interrupts
return 0;
}
cur = Proc_GetCurThread();
// Create new thread
- new = malloc( sizeof(tThread) );
+ new = Threads_CloneThreadZero();
if(!new) {
Warning("Proc_SpawnWorker - Out of heap space!\n");
return -1;
}
- memcpy(new, &gThreadZero, sizeof(tThread));
- // Set Thread ID
- new->TID = giNextTID++;
// Create a new worker stack (in PID0's address space)
- // The stack is relocated by this code
+ // - The stack is relocated by this function
new->KernelStack = MM_NewWorkerStack();
// Get ESP and EBP based in the new stack
new->SavedState.EBP = ebp;
eip = GetEIP();
if(eip == SWITCH_MAGIC) {
- outb(0x20, 0x20); // ACK Timer and return as child
+ __asm__ __volatile__ ("mov %0, %%db0" : : "r"(new));
+ #if USE_MP
+ // ACK the interrupt
+ if(GetCPUNum())
+ gpMP_LocalAPIC->EOI.Val = 0;
+ else
+ #endif
+ outb(0x20, 0x20); // ACK Timer and return as child
+ __asm__ __volatile__ ("sti"); // Restart interrupts
return 0;
}
// Set EIP as parent
new->SavedState.EIP = eip;
// Mark as active
- new->Status = THREAD_STAT_ACTIVE;
Threads_AddActive( new );
return new->TID;
// Allocate Stack - Allocate incrementally to clean up MM_Dump output
for( i = 0; i < USER_STACK_SZ/0x1000; i++ )
- MM_Allocate( base + (i<<12) );
+ {
+ if( !MM_Allocate( base + (i<<12) ) )
+ {
+ Warning("OOM: Proc_MakeUserStack");
+ return 0;
+ }
+ }
return base + USER_STACK_SZ;
}
{
// Rewinds the stack and calls the user function
// Never returns
- __asm__ __volatile__ ("mov %0, %%ebp;\n\tcall Proc_ReturnToUser" :: "r"(Thread->FaultHandler));
+ Proc_ReturnToUser( Thread->FaultHandler, Thread->CurFaultNum, Thread->KernelStack );
for(;;);
}
// If the spinlock is set, let it complete
if(IS_LOCKED(&glThreadListLock)) return;
- // Clear Delete Queue
- while(gDeleteThreads)
- {
- thread = gDeleteThreads->Next;
- if(gDeleteThreads->IsLocked) { // Only free if structure is unused
- gDeleteThreads->Status = THREAD_STAT_NULL;
- free( gDeleteThreads );
- }
- gDeleteThreads = thread;
- }
-
- // Check if there is any tasks running
- if(giNumActiveThreads == 0) {
- #if 0
- Log("No Active threads, sleeping");
- #endif
- #if USE_MP
- if(CPU)
- gpMP_LocalAPIC->EOI.Val = 0;
- else
- #endif
- outb(0x20, 0x20);
- __asm__ __volatile__ ("hlt");
- return;
- }
-
// Get current thread
#if USE_MP
thread = gaCPUs[CPU].Current;
thread = gCurrentThread;
#endif
- // Reduce remaining quantum and continue timeslice if non-zero
- if(thread->Remaining--) return;
- // Reset quantum for next call
- thread->Remaining = thread->Quantum;
-
- // Get machine state
- __asm__ __volatile__ ("mov %%esp, %0":"=r"(esp));
- __asm__ __volatile__ ("mov %%ebp, %0":"=r"(ebp));
- eip = GetEIP();
- if(eip == SWITCH_MAGIC) return; // Check if a switch happened
-
- // Save machine state
- thread->SavedState.ESP = esp;
- thread->SavedState.EBP = ebp;
- thread->SavedState.EIP = eip;
+ if( thread )
+ {
+ // Reduce remaining quantum and continue timeslice if non-zero
+ if( thread->Remaining-- )
+ return;
+ // Reset quantum for next call
+ thread->Remaining = thread->Quantum;
+
+ // Get machine state
+ __asm__ __volatile__ ( "mov %%esp, %0" : "=r" (esp) );
+ __asm__ __volatile__ ( "mov %%ebp, %0" : "=r" (ebp) );
+ eip = GetEIP();
+ if(eip == SWITCH_MAGIC) return; // Check if a switch happened
+
+ // Save machine state
+ thread->SavedState.ESP = esp;
+ thread->SavedState.EBP = ebp;
+ thread->SavedState.EIP = eip;
+ }
// Get next thread to run
thread = Threads_GetNextToRun(CPU, thread);
- // Error Check
+ // No avaliable tasks, just go into low power mode (idle thread)
if(thread == NULL) {
- Warning("Hmm... Threads_GetNextToRun returned NULL, I don't think this should happen.\n");
- return;
+ #if USE_MP
+ thread = gaCPUs[CPU].IdleThread;
+ Log("CPU %i Running Idle Thread", CPU);
+ #else
+ thread = gpIdleThread;
+ #endif
}
+ // Set current thread
+ #if USE_MP
+ gaCPUs[CPU].Current = thread;
+ #else
+ gCurrentThread = thread;
+ #endif
+
#if DEBUG_TRACE_SWITCH
Log("Switching to task %i, CR3 = 0x%x, EIP = %p",
thread->TID,
);
#endif
- // Set current thread
- #if USE_MP
- gaCPUs[CPU].Current->bIsRunning = 0;
- gaCPUs[CPU].Current = thread;
- thread->bIsRunning = 1;
- #else
- gCurrentThread = thread;
+ #if USE_MP // MP Debug
+ Log("CPU = %i, Thread %p", CPU, thread);
#endif
- //Log("CPU = %i", CPU);
-
// Update Kernel Stack pointer
gTSSs[CPU].ESP0 = thread->KernelStack-4;
- // Set address space
- #if USE_PAE
- # error "Todo: Implement PAE Address space switching"
- #else
- __asm__ __volatile__ ("mov %0, %%cr3"::"a"(thread->MemState.CR3));
- #endif
-
#if 0
if(thread->SavedState.ESP > 0xC0000000
&& thread->SavedState.ESP < thread->KernelStack-0x2000) {
}
#endif
+ #if USE_PAE
+ # error "Todo: Implement PAE Address space switching"
+ #else
// Switch threads
__asm__ __volatile__ (
+ "mov %4, %%cr3\n\t" // Set address space
"mov %1, %%esp\n\t" // Restore ESP
"mov %2, %%ebp\n\t" // and EBP
"jmp *%3" : : // And return to where we saved state (Proc_Clone or Proc_Scheduler)
"a"(SWITCH_MAGIC), "b"(thread->SavedState.ESP),
- "d"(thread->SavedState.EBP), "c"(thread->SavedState.EIP)
+ "d"(thread->SavedState.EBP), "c"(thread->SavedState.EIP),
+ "r"(thread->MemState.CR3)
);
+ #endif
for(;;); // Shouldn't reach here
}