#include #include #include #include #include #include #include #include "../kernel.h" #if defined(a64) #include "../Architecture/amd64/cpu/apic.hpp" #include "../Architecture/amd64/cpu/gdt.hpp" #elif defined(a32) #include "../Architecture/i686/cpu/apic.hpp" #elif defined(aa64) #endif NewLock(SchedulerLock); /* FIXME: On screen task manager is corrupting the stack... */ // #define ON_SCREEN_SCHEDULER_TASK_MANAGER 1 // #define DEBUG_SCHEDULER 1 // #define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1 // #define DEBUG_GET_NEXT_AVAILABLE_THREAD 1 // #define DEBUG_FIND_NEW_PROCESS 1 // #define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1 // #define DEBUG_WAKE_UP_THREADS 1 /* Global */ #ifdef DEBUG_SCHEDULER #define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1 #define DEBUG_GET_NEXT_AVAILABLE_THREAD 1 #define DEBUG_FIND_NEW_PROCESS 1 #define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1 #define DEBUG_WAKE_UP_THREADS 1 #define schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define schedbg(m, ...) #endif /* GetNextAvailableThread */ #ifdef DEBUG_GET_NEXT_AVAILABLE_PROCESS #define gnap_schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define gnap_schedbg(m, ...) #endif /* GetNextAvailableProcess */ #ifdef DEBUG_GET_NEXT_AVAILABLE_THREAD #define gnat_schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define gnat_schedbg(m, ...) #endif /* FindNewProcess */ #ifdef DEBUG_FIND_NEW_PROCESS #define fnp_schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define fnp_schedbg(m, ...) #endif /* SchedulerSearchProcessThread */ #ifdef DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD #define sspt_schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define sspt_schedbg(m, ...) #endif /* WakeUpThreads */ #ifdef DEBUG_WAKE_UP_THREADS #define wut_schedbg(m, ...) \ debug(m, ##__VA_ARGS__); \ __sync #else #define wut_schedbg(m, ...) #endif extern "C" SafeFunction NIF void TaskingScheduler_OneShot(int TimeSlice) { if (TimeSlice == 0) TimeSlice = Tasking::TaskPriority::Normal; #if defined(a64) ((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice); #elif defined(a32) #elif defined(aa64) #endif } namespace Tasking { #if defined(a64) SafeFunction NIF bool Task::FindNewProcess(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; fnp_schedbg("%d processes", ListProcess.size()); #ifdef DEBUG_FIND_NEW_PROCESS foreach (auto process in ListProcess) fnp_schedbg("Process %d %s", process->ID, process->Name); #endif foreach (auto process in ListProcess) { if (InvalidPCB(process)) continue; switch (process->Status) { case TaskStatus::Ready: fnp_schedbg("Ready process (%s)%d", process->Name, process->ID); break; default: fnp_schedbg("Process \"%s\"(%d) status %d", process->Name, process->ID, process->Status); /* We don't actually remove the process. RemoveProcess firstly checks if it's terminated, if not, it will loop through Threads and call RemoveThread on terminated threads. */ RemoveProcess(process); continue; } foreach (auto thread in process->Threads) { if (InvalidTCB(thread)) continue; if (thread->Status != TaskStatus::Ready) continue; if (thread->Info.Affinity[CurrentCPU->ID] == false) continue; CurrentCPU->CurrentProcess = process; CurrentCPU->CurrentThread = thread; return true; } } fnp_schedbg("No process to run."); return false; } SafeFunction NIF bool Task::GetNextAvailableThread(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; for (size_t i = 0; i < CurrentCPU->CurrentProcess->Threads.size(); i++) { if (CurrentCPU->CurrentProcess->Threads[i] == CurrentCPU->CurrentThread.Load()) { size_t TempIndex = i; RetryAnotherThread: TCB *nextThread = CurrentCPU->CurrentProcess->Threads[TempIndex + 1]; if (unlikely(InvalidTCB(nextThread))) { if (TempIndex > CurrentCPU->CurrentProcess->Threads.size()) break; TempIndex++; gnat_schedbg("Thread %#lx is invalid", nextThread); goto RetryAnotherThread; } gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)", CurrentCPU->CurrentProcess->Threads[i]->Name, CurrentCPU->CurrentProcess->Threads[i]->ID, thread->Name, thread->ID); if (nextThread->Status != TaskStatus::Ready) { gnat_schedbg("Thread %d is not ready", nextThread->ID); TempIndex++; goto RetryAnotherThread; } if (nextThread->Info.Affinity[CurrentCPU->ID] == false) continue; CurrentCPU->CurrentThread = nextThread; gnat_schedbg("[thd 0 -> end] Scheduling thread %d parent of %s->%d Procs %d", thread->ID, thread->Parent->Name, CurrentCPU->CurrentProcess->Threads.size(), ListProcess.size()); return true; } #ifdef DEBUG else { gnat_schedbg("Thread %d is not the current one", CurrentCPU->CurrentProcess->Threads[i]->ID); } #endif } return false; } SafeFunction NIF bool Task::GetNextAvailableProcess(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; bool Skip = true; foreach (auto process in ListProcess) { if (process == CurrentCPU->CurrentProcess.Load()) { Skip = false; gnap_schedbg("Found current process %#lx", process); continue; } if (Skip) { gnap_schedbg("Skipping process %#lx", process); continue; } if (InvalidPCB(process)) { gnap_schedbg("Invalid process %#lx", process); continue; } if (process->Status != TaskStatus::Ready) { gnap_schedbg("Process %d is not ready", process->ID); continue; } foreach (auto thread in process->Threads) { if (InvalidTCB(thread)) { gnap_schedbg("Invalid thread %#lx", thread); continue; } if (thread->Status != TaskStatus::Ready) { gnap_schedbg("Thread %d is not ready", thread->ID); continue; } if (thread->Info.Affinity[CurrentCPU->ID] == false) continue; CurrentCPU->CurrentProcess = process; CurrentCPU->CurrentThread = thread; gnap_schedbg("[cur proc+1 -> first thd] Scheduling thread %d %s->%d (Total Procs %d)", thread->ID, thread->Name, process->Threads.size(), ListProcess.size()); return true; } } gnap_schedbg("No process to run."); return false; } SafeFunction NIF void Task::SchedulerCleanupProcesses() { foreach (auto process in ListProcess) { if (InvalidPCB(process)) continue; RemoveProcess(process); } } SafeFunction NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; foreach (auto process in ListProcess) { if (InvalidPCB(process)) { sspt_schedbg("Invalid process %#lx", process); continue; } if (process->Status != TaskStatus::Ready) { sspt_schedbg("Process %d is not ready", process->ID); continue; } foreach (auto thread in process->Threads) { if (InvalidTCB(thread)) { sspt_schedbg("Invalid thread %#lx", thread); continue; } if (thread->Status != TaskStatus::Ready) { sspt_schedbg("Thread %d is not ready", thread->ID); continue; } if (thread->Info.Affinity[CurrentCPU->ID] == false) continue; CurrentCPU->CurrentProcess = process; CurrentCPU->CurrentThread = thread; sspt_schedbg("[proc 0 -> end -> first thd] Scheduling thread %d parent of %s->%d (Procs %d)", thread->ID, thread->Parent->Name, process->Threads.size(), ListProcess.size()); return true; } } return false; } SafeFunction NIF void Task::UpdateProcessStatus() { foreach (auto process in ListProcess) { if (InvalidPCB(process)) continue; if (process->Status == TaskStatus::Terminated || process->Status == TaskStatus::Stopped) continue; bool AllThreadsSleeping = true; foreach (auto thread in process->Threads) { if (thread->Status != TaskStatus::Sleeping) { AllThreadsSleeping = false; break; } } if (AllThreadsSleeping) process->Status = TaskStatus::Sleeping; else if (process->Status == TaskStatus::Sleeping) process->Status = TaskStatus::Ready; } } SafeFunction NIF void Task::WakeUpThreads(void *CPUDataPointer) { CPUData *CurrentCPU = (CPUData *)CPUDataPointer; foreach (auto process in ListProcess) { if (InvalidPCB(process)) continue; if (process->Status == TaskStatus::Terminated || process->Status == TaskStatus::Stopped) continue; foreach (auto thread in process->Threads) { if (InvalidTCB(thread)) continue; if (thread->Status != TaskStatus::Sleeping) continue; /* Check if the thread is ready to wake up. */ if (thread->Info.SleepUntil < TimeManager->GetCounter()) { if (process->Status == TaskStatus::Sleeping) process->Status = TaskStatus::Ready; thread->Status = TaskStatus::Ready; thread->Info.SleepUntil = 0; wut_schedbg("Thread \"%s\"(%d) woke up.", thread->Name, thread->ID); } else { wut_schedbg("Thread \"%s\"(%d) is not ready to wake up. (SleepUntil: %d, Counter: %d)", thread->Name, thread->ID, thread->Info.SleepUntil, TimeManager->GetCounter()); } } } } #ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER int SuccessSource = 0; int sanity; const char *Statuses[] = { "FF0000", /* Unknown */ "AAFF00", /* Ready */ "00AA00", /* Running */ "FFAA00", /* Sleeping */ "FFAA00", /* Waiting */ "FF0088", /* Stopped */ "FF0000", /* Terminated */ }; const char *StatusesSign[] = { "Unknown", "Ready", "Run", "Sleep", "Wait", "Stop", "Terminated", }; const char *SuccessSourceStrings[] = { "Unknown", "GetNextAvailableThread", "GetNextAvailableProcess", "SchedulerSearchProcessThread", }; SafeFunction NIF void OnScreenTaskManagerUpdate() { TimeManager->Sleep(100); Video::ScreenBuffer *sb = Display->GetBuffer(0); for (short i = 0; i < 340; i++) { for (short j = 0; j < 200; j++) { uint32_t *Pixel = (uint32_t *)((uintptr_t)sb->Buffer + (j * sb->Width + i) * (bInfo->Framebuffer[0].BitsPerPixel / 8)); *Pixel = 0x222222; } } uint32_t tmpX, tmpY; Display->GetBufferCursor(0, &tmpX, &tmpY); Display->SetBufferCursor(0, 0, 0); printf("\eF02C21Task Manager\n"); foreach (auto Proc in TaskManager->GetProcessList()) { int Status = Proc->Status; printf("\e%s-> \eAABBCC%s \e00AAAA%s\n", Statuses[Status], Proc->Name, StatusesSign[Status]); foreach (auto Thd in Proc->Threads) { Status = Thd->Status; printf(" \e%s-> \eAABBCC%s \e00AAAA%s\n\eAABBCC", Statuses[Status], Thd->Name, StatusesSign[Status]); } } register uintptr_t CurrentStackAddress asm("rsp"); printf("Sanity: %d, Stack: %#lx\nSched. Source: %s", sanity++, CurrentStackAddress, SuccessSourceStrings[SuccessSource]); if (sanity > 1000) sanity = 0; Display->SetBufferCursor(0, tmpX, tmpY); Display->SetBuffer(0); TimeManager->Sleep(100); } #endif SafeFunction NIF void Task::Schedule(CPU::x64::TrapFrame *Frame) { if (StopScheduler) { warn("Scheduler stopped."); return; } CPU::x64::writecr3({.raw = (uint64_t)KernelPageTable}); /* Restore kernel page table for safety reasons. */ uint64_t SchedTmpTicks = CPU::Counter(); this->LastTaskTicks.Store(SchedTmpTicks - this->SchedulerTicks.Load()); CPUData *CurrentCPU = GetCurrentCPU(); schedbg("Scheduler called on CPU %d.", CurrentCPU->ID); schedbg("%d: %ld%%", CurrentCPU->ID, GetUsage(CurrentCPU->ID)); #ifdef DEBUG_SCHEDULER { schedbg("================================================================"); schedbg("Status: 0-ukn | 1-rdy | 2-run | 3-wait | 4-term"); schedbg("Technical Informations on regs %#lx", Frame->InterruptNumber); size_t ds; asmv("mov %%ds, %0" : "=r"(ds)); schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx", CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), Frame->ss, Frame->cs, ds); schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx", Frame->r8, Frame->r9, Frame->r10, Frame->r11); schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx", Frame->r12, Frame->r13, Frame->r14, Frame->r15); schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx", Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); schedbg("================================================================"); } #endif if (unlikely(InvalidPCB(CurrentCPU->CurrentProcess.Load()) || InvalidTCB(CurrentCPU->CurrentThread.Load()))) { schedbg("Invalid process or thread. Finding a new one."); if (this->FindNewProcess(CurrentCPU)) goto Success; else goto Idle; } else { CurrentCPU->CurrentThread->Registers = *Frame; CPU::x64::fxsave(CurrentCPU->CurrentThread->FPU); CurrentCPU->CurrentThread->GSBase = CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE); CurrentCPU->CurrentThread->FSBase = CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE); if (CurrentCPU->CurrentProcess->Status == TaskStatus::Running) CurrentCPU->CurrentProcess->Status = TaskStatus::Ready; if (CurrentCPU->CurrentThread->Status == TaskStatus::Running) CurrentCPU->CurrentThread->Status = TaskStatus::Ready; this->UpdateProcessStatus(); schedbg("Passed UpdateProcessStatus"); this->WakeUpThreads(CurrentCPU); schedbg("Passed WakeUpThreads"); if (this->GetNextAvailableThread(CurrentCPU)) { #ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER SuccessSource = 1; #endif goto Success; } schedbg("Passed GetNextAvailableThread"); if (this->GetNextAvailableProcess(CurrentCPU)) { #ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER SuccessSource = 2; #endif goto Success; } schedbg("Passed GetNextAvailableProcess"); this->SchedulerCleanupProcesses(); schedbg("Passed SchedulerCleanupProcesses"); if (SchedulerSearchProcessThread(CurrentCPU)) { #ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER SuccessSource = 3; #endif schedbg("Passed SchedulerSearchProcessThread"); goto Success; } else { schedbg("SchedulerSearchProcessThread failed. Going idle."); goto Idle; } } /* [this]->RealEnd */ warn("Unwanted reach!"); TaskingScheduler_OneShot(100); goto RealEnd; /* Idle-->Success */ Idle: CurrentCPU->CurrentProcess = IdleProcess; CurrentCPU->CurrentThread = IdleThread; /* Success-->End */ Success: schedbg("Process \"%s\"(%d) Thread \"%s\"(%d) is now running on CPU %d", CurrentCPU->CurrentProcess->Name, CurrentCPU->CurrentProcess->ID, CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID, CurrentCPU->ID); CurrentCPU->CurrentProcess->Status = TaskStatus::Running; CurrentCPU->CurrentThread->Status = TaskStatus::Running; *Frame = CurrentCPU->CurrentThread->Registers; for (size_t i = 0; i < sizeof(CurrentCPU->CurrentThread->IPHistory) / sizeof(CurrentCPU->CurrentThread->IPHistory[0]); i++) CurrentCPU->CurrentThread->IPHistory[i + 1] = CurrentCPU->CurrentThread->IPHistory[i]; CurrentCPU->CurrentThread->IPHistory[0] = Frame->rip; GlobalDescriptorTable::SetKernelStack((void *)((uintptr_t)CurrentCPU->CurrentThread->Stack->GetStackTop())); CPU::x64::writecr3({.raw = (uint64_t)CurrentCPU->CurrentProcess->PageTable}); /* Not sure if this is needed, but it's better to be safe than sorry. */ asmv("movq %cr3, %rax"); asmv("movq %rax, %cr3"); CPU::x64::fxrstor(CurrentCPU->CurrentThread->FPU); CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase); CPU::x64::wrmsr(CPU::x64::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase); #ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER OnScreenTaskManagerUpdate(); #endif switch (CurrentCPU->CurrentProcess->Security.TrustLevel) { case TaskTrustLevel::System: case TaskTrustLevel::Kernel: // wrmsr(MSR_SHADOW_GS_BASE, (uint64_t)CurrentCPU->CurrentThread); break; case TaskTrustLevel::User: // wrmsr(MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->gs); break; default: error("Unknown trust level %d.", CurrentCPU->CurrentProcess->Security.TrustLevel); break; } /* End-->RealEnd */ // End: /* TODO: This is not accurate. */ if (CurrentCPU->CurrentProcess->Security.TrustLevel == TaskTrustLevel::User) UpdateUserTime(&CurrentCPU->CurrentProcess->Info); else UpdateKernelTime(&CurrentCPU->CurrentProcess->Info); if (CurrentCPU->CurrentThread->Security.TrustLevel == TaskTrustLevel::User) UpdateUserTime(&CurrentCPU->CurrentThread->Info); else UpdateKernelTime(&CurrentCPU->CurrentThread->Info); UpdateUsage(&CurrentCPU->CurrentProcess->Info, CurrentCPU->ID); UpdateUsage(&CurrentCPU->CurrentThread->Info, CurrentCPU->ID); TaskingScheduler_OneShot(CurrentCPU->CurrentThread->Info.Priority); if (CurrentCPU->CurrentThread->Security.IsDebugEnabled && CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled) trace("%s[%ld]: RIP=%#lx RBP=%#lx RSP=%#lx", CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID, CurrentCPU->CurrentThread->Registers.rip, CurrentCPU->CurrentThread->Registers.rbp, CurrentCPU->CurrentThread->Registers.rsp); schedbg("================================================================"); schedbg("Technical Informations on Thread %s[%ld]:", CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID); uint64_t ds; asmv("mov %%ds, %0" : "=r"(ds)); schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx", CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE), Frame->ss, Frame->cs, ds); schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx", Frame->r8, Frame->r9, Frame->r10, Frame->r11); schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx", Frame->r12, Frame->r13, Frame->r14, Frame->r15); schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx); schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp); schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx", Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode); schedbg("================================================================"); /* RealEnd->[Function Exit] */ RealEnd: this->SchedulerTicks.Store(CPU::Counter() - SchedTmpTicks); __sync; /* TODO: Is this really needed? */ } SafeFunction NIF void Task::OnInterruptReceived(CPU::x64::TrapFrame *Frame) { SmartCriticalSection(SchedulerLock); this->Schedule(Frame); } #elif defined(a32) SafeFunction bool Task::FindNewProcess(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction bool Task::GetNextAvailableThread(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction bool Task::GetNextAvailableProcess(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction void Task::SchedulerCleanupProcesses() { fixme("unimplemented"); } SafeFunction bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction void Task::Schedule(void *Frame) { fixme("unimplemented"); } SafeFunction void Task::OnInterruptReceived(CPU::x32::TrapFrame *Frame) { this->Schedule(Frame); } #elif defined(aa64) SafeFunction bool Task::FindNewProcess(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction bool Task::GetNextAvailableThread(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction bool Task::GetNextAvailableProcess(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction void Task::SchedulerCleanupProcesses() { fixme("unimplemented"); } SafeFunction bool Task::SchedulerSearchProcessThread(void *CPUDataPointer) { fixme("unimplemented"); } SafeFunction void Task::Schedule(void *Frame) { fixme("unimplemented"); } SafeFunction void Task::OnInterruptReceived(void *Frame) { this->Schedule(Frame); } #endif }