mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-07-17 10:11:45 +00:00
.github
.vscode
Architecture
Core
Execute
FileSystem
Library
Profiling
Recovery
SystemCalls
Tasking
InterProcessCommunication.cpp
Security.cpp
Task.cpp
include
.gitignore
DAPI.hpp
Doxyfile
Fex.hpp
KConfig.cpp
KThread.cpp
Kernel.cpp
LICENSE
Makefile
README.md
dump.sh
kernel.h
syscalls.h
1139 lines
42 KiB
C++
1139 lines
42 KiB
C++
#include <task.hpp>
|
|
|
|
#include <dumper.hpp>
|
|
#include <convert.h>
|
|
#include <lock.hpp>
|
|
#include <printf.h>
|
|
#include <smp.hpp>
|
|
|
|
#include "../kernel.h"
|
|
|
|
#if defined(__amd64__)
|
|
#include "../Architecture/amd64/cpu/apic.hpp"
|
|
#include "../Architecture/amd64/cpu/gdt.hpp"
|
|
#elif defined(__i386__)
|
|
#include "../Architecture/i686/cpu/apic.hpp"
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
|
|
// #define DEBUG_SCHEDULER 1
|
|
|
|
#ifdef DEBUG_SCHEDULER
|
|
#define schedbg(m, ...) debug(m, ##__VA_ARGS__)
|
|
#else
|
|
#define schedbg(m, ...)
|
|
#endif
|
|
|
|
NewLock(TaskingLock);
|
|
NewLock(SchedulerLock);
|
|
|
|
namespace Tasking
|
|
{
|
|
extern "C" SafeFunction __no_instrument_function void OneShot(int TimeSlice)
|
|
{
|
|
if (TimeSlice == 0)
|
|
TimeSlice = 10;
|
|
#if defined(__amd64__)
|
|
((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x64::IRQ16, TimeSlice);
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
}
|
|
|
|
void Task::Schedule()
|
|
{
|
|
if (!StopScheduler)
|
|
OneShot(100);
|
|
// APIC::InterruptCommandRegisterLow icr;
|
|
// icr.Vector = CPU::x64::IRQ16;
|
|
// icr.Level = APIC::APICLevel::Assert;
|
|
// ((APIC::APIC *)Interrupts::apic[0])->IPI(GetCurrentCPU()->ID, icr);
|
|
}
|
|
|
|
__naked __used __no_stack_protector __no_instrument_function void IdleProcessLoop()
|
|
{
|
|
#if defined(__amd64__) || defined(__i386__)
|
|
asmv("IdleLoop:\n"
|
|
"hlt\n"
|
|
"jmp IdleLoop\n");
|
|
#elif defined(__aarch64__)
|
|
asmv("IdleLoop:\n"
|
|
"wfe\n"
|
|
"b IdleLoop\n");
|
|
#endif
|
|
}
|
|
|
|
SafeFunction __no_instrument_function bool Task::InvalidPCB(PCB *pcb)
|
|
{
|
|
if (!pcb)
|
|
return true;
|
|
if (pcb >= (PCB *)0xfffffffffffff000)
|
|
return true;
|
|
if (!Memory::Virtual().Check((void *)pcb))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function bool Task::InvalidTCB(TCB *tcb)
|
|
{
|
|
if (!tcb)
|
|
return true;
|
|
if (tcb >= (TCB *)0xfffffffffffff000)
|
|
return true;
|
|
if (!Memory::Virtual().Check((void *)tcb))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::RemoveThread(TCB *Thread)
|
|
{
|
|
for (uint64_t i = 0; i < Thread->Parent->Threads.size(); i++)
|
|
if (Thread->Parent->Threads[i] == Thread)
|
|
{
|
|
trace("Thread \"%s\"(%d) removed from process \"%s\"(%d)",
|
|
Thread->Name, Thread->ID, Thread->Parent->Name, Thread->Parent->ID);
|
|
// Free memory
|
|
delete Thread->Stack;
|
|
SecurityManager.DestroyToken(Thread->Security.UniqueToken);
|
|
delete Thread->Parent->Threads[i];
|
|
// Remove from the list
|
|
Thread->Parent->Threads.remove(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::RemoveProcess(PCB *Process)
|
|
{
|
|
if (Process == nullptr)
|
|
return;
|
|
|
|
if (Process->Status == Terminated)
|
|
{
|
|
foreach (TCB *thread in Process->Threads)
|
|
RemoveThread(thread);
|
|
|
|
foreach (PCB *process in Process->Children)
|
|
RemoveProcess(process);
|
|
|
|
for (uint64_t i = 0; i < ListProcess.size(); i++)
|
|
{
|
|
if (ListProcess[i] == Process)
|
|
{
|
|
trace("Process \"%s\"(%d) removed from the list", Process->Name, Process->ID);
|
|
// Free memory
|
|
delete ListProcess[i]->IPCHandles;
|
|
SecurityManager.DestroyToken(ListProcess[i]->Security.UniqueToken);
|
|
if (ListProcess[i]->Security.TrustLevel == TaskTrustLevel::User)
|
|
KernelAllocator.FreePages((void *)ListProcess[i]->PageTable, TO_PAGES(PAGE_SIZE));
|
|
delete ListProcess[i];
|
|
// Remove from the list
|
|
ListProcess.remove(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (TCB *thread in Process->Threads)
|
|
if (thread->Status == Terminated)
|
|
RemoveThread(thread);
|
|
}
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::UpdateUserTime(TaskInfo *Info)
|
|
{
|
|
// TODO
|
|
Info->UserTime++;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::UpdateKernelTime(TaskInfo *Info)
|
|
{
|
|
// TODO
|
|
Info->KernelTime++;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::UpdateUsage(TaskInfo *Info, int Core)
|
|
{
|
|
if (Info->Affinity[Core] == true)
|
|
{
|
|
// TODO: Not working(?)
|
|
uint64_t CounterNow = CPU::Counter();
|
|
|
|
Info->OldUserTime = Info->CurrentUserTime;
|
|
Info->OldKernelTime = Info->CurrentKernelTime;
|
|
|
|
Info->CurrentUserTime = Info->UserTime;
|
|
Info->CurrentKernelTime = Info->KernelTime;
|
|
|
|
Info->Usage[Core] = (Info->CurrentUserTime - Info->OldUserTime) + (Info->CurrentKernelTime - Info->OldKernelTime);
|
|
Info->Usage[Core] = (Info->Usage[Core] * 100) / (CounterNow - Info->SpawnTime);
|
|
|
|
Info->OldUserTime = Info->CurrentUserTime;
|
|
Info->OldKernelTime = Info->CurrentKernelTime;
|
|
|
|
Info->CurrentUserTime = Info->UserTime;
|
|
Info->CurrentKernelTime = Info->KernelTime;
|
|
}
|
|
}
|
|
|
|
#if defined(__amd64__)
|
|
SafeFunction __no_instrument_function bool Task::FindNewProcess(void *CPUDataPointer)
|
|
{
|
|
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
|
schedbg("%d processes", ListProcess.size());
|
|
#ifdef DEBUG_SCHEDULER
|
|
foreach (auto var in ListProcess)
|
|
{
|
|
schedbg("Process %d %s", var->ID, var->Name);
|
|
}
|
|
#endif
|
|
// Find a new process to execute.
|
|
foreach (PCB *pcb in ListProcess)
|
|
{
|
|
if (InvalidPCB(pcb))
|
|
continue;
|
|
|
|
// Check process status.
|
|
switch (pcb->Status)
|
|
{
|
|
case TaskStatus::Ready:
|
|
schedbg("Ready process (%s)%d", pcb->Name, pcb->ID);
|
|
break;
|
|
default:
|
|
schedbg("Process \"%s\"(%d) status %d", pcb->Name, pcb->ID, pcb->Status);
|
|
RemoveProcess(pcb);
|
|
continue;
|
|
}
|
|
|
|
// Get first available thread from the list.
|
|
foreach (TCB *tcb in pcb->Threads)
|
|
{
|
|
if (InvalidTCB(tcb))
|
|
continue;
|
|
|
|
if (tcb->Status != TaskStatus::Ready)
|
|
continue;
|
|
|
|
// Set process and thread as the current one's.
|
|
CurrentCPU->CurrentProcess = pcb;
|
|
CurrentCPU->CurrentThread = tcb;
|
|
// Success!
|
|
return true;
|
|
}
|
|
}
|
|
schedbg("No process to run.");
|
|
// No process found. Idling...
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function bool Task::GetNextAvailableThread(void *CPUDataPointer)
|
|
{
|
|
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
|
|
|
for (uint64_t i = 0; i < CurrentCPU->CurrentProcess->Threads.size(); i++)
|
|
{
|
|
// Loop until we find the current thread from the process thread list.
|
|
if (CurrentCPU->CurrentProcess->Threads[i] == CurrentCPU->CurrentThread)
|
|
{
|
|
// Check if the next thread is valid. If not, we search until we find, but if we reach the end of the list, we go to the next process.
|
|
uint64_t tmpidx = i;
|
|
RetryAnotherThread:
|
|
TCB *thread = CurrentCPU->CurrentProcess->Threads[tmpidx + 1];
|
|
if (InvalidTCB(thread))
|
|
{
|
|
if (tmpidx > CurrentCPU->CurrentProcess->Threads.size())
|
|
break;
|
|
tmpidx++;
|
|
goto RetryAnotherThread;
|
|
}
|
|
|
|
schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)", CurrentCPU->CurrentProcess->Threads[i]->Name, CurrentCPU->CurrentProcess->Threads[i]->ID, thread->Name, thread->ID);
|
|
|
|
// Check if the thread is ready to be executed.
|
|
if (thread->Status != TaskStatus::Ready)
|
|
{
|
|
schedbg("Thread %d is not ready", thread->ID);
|
|
goto RetryAnotherThread;
|
|
}
|
|
|
|
// Everything is fine, we can set the new thread as the current one.
|
|
CurrentCPU->CurrentThread = thread;
|
|
schedbg("[thd 0 -> end] Scheduling thread %d parent of %s->%d Procs %d", thread->ID, thread->Parent->Name, CurrentCPU->CurrentProcess->Threads.size(), ListProcess.size());
|
|
// Yay! We found a new thread to execute.
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function bool Task::GetNextAvailableProcess(void *CPUDataPointer)
|
|
{
|
|
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
|
|
|
for (uint64_t i = 0; i < ListProcess.size(); i++)
|
|
{
|
|
// Loop until we find the current process from the process list.
|
|
if (ListProcess[i] == CurrentCPU->CurrentProcess)
|
|
{
|
|
// Check if the next process is valid. If not, we search until we find.
|
|
uint64_t tmpidx = i;
|
|
RetryAnotherProcess:
|
|
PCB *pcb = ListProcess[tmpidx + 1];
|
|
if (InvalidPCB(pcb))
|
|
{
|
|
if (tmpidx > ListProcess.size())
|
|
break;
|
|
tmpidx++;
|
|
goto RetryAnotherProcess;
|
|
}
|
|
|
|
if (pcb->Status != TaskStatus::Ready)
|
|
goto RetryAnotherProcess;
|
|
|
|
// Everything good, now search for a thread.
|
|
for (uint64_t j = 0; j < pcb->Threads.size(); j++)
|
|
{
|
|
TCB *tcb = pcb->Threads[j];
|
|
if (InvalidTCB(tcb))
|
|
continue;
|
|
if (tcb->Status != TaskStatus::Ready)
|
|
continue;
|
|
// Success! We set as the current one and restore the stuff.
|
|
CurrentCPU->CurrentProcess = pcb;
|
|
CurrentCPU->CurrentThread = tcb;
|
|
schedbg("[cur proc+1 -> first thd] Scheduling thread %d %s->%d (Total Procs %d)", tcb->ID, tcb->Name, pcb->Threads.size(), ListProcess.size());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::SchedulerCleanupProcesses()
|
|
{
|
|
foreach (PCB *pcb in ListProcess)
|
|
{
|
|
if (InvalidPCB(pcb))
|
|
continue;
|
|
RemoveProcess(pcb);
|
|
}
|
|
}
|
|
|
|
SafeFunction __no_instrument_function bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
|
|
{
|
|
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
|
|
|
foreach (PCB *pcb in ListProcess)
|
|
{
|
|
if (InvalidPCB(pcb))
|
|
continue;
|
|
if (pcb->Status != TaskStatus::Ready)
|
|
continue;
|
|
|
|
// Now do the thread search!
|
|
foreach (TCB *tcb in pcb->Threads)
|
|
{
|
|
if (InvalidTCB(tcb))
|
|
continue;
|
|
if (tcb->Status != TaskStatus::Ready)
|
|
continue;
|
|
// \o/ We found a new thread to execute.
|
|
CurrentCPU->CurrentProcess = pcb;
|
|
CurrentCPU->CurrentThread = tcb;
|
|
schedbg("[proc 0 -> end -> first thd] Scheduling thread %d parent of %s->%d (Procs %d)", tcb->ID, tcb->Parent->Name, pcb->Threads.size(), ListProcess.size());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::Schedule(CPU::x64::TrapFrame *Frame)
|
|
{
|
|
SmartCriticalSection(SchedulerLock);
|
|
if (StopScheduler)
|
|
{
|
|
warn("Scheduler stopped.");
|
|
return;
|
|
}
|
|
CPU::x64::writecr3({.raw = (uint64_t)KernelPageTable}); // Restore kernel page table for safety reasons.
|
|
CPUData *CurrentCPU = GetCurrentCPU();
|
|
// if (CurrentCPU->ID != 0)
|
|
// debug("Scheduler called from CPU %d", CurrentCPU->ID);
|
|
schedbg("Scheduler called on CPU %d.", CurrentCPU->ID);
|
|
schedbg("%d: %ld%%", CurrentCPU->ID, GetUsage(CurrentCPU->ID));
|
|
|
|
{
|
|
schedbg("================================================================");
|
|
schedbg("Status: 0-ukn | 1-rdy | 2-run | 3-wait | 4-term");
|
|
schedbg("Technical Informations on regs %#lx", Frame->InterruptNumber);
|
|
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("================================================================");
|
|
}
|
|
|
|
// Null or invalid process/thread? Let's find a new one to execute.
|
|
if (InvalidPCB(CurrentCPU->CurrentProcess) || InvalidTCB(CurrentCPU->CurrentThread))
|
|
{
|
|
if (this->FindNewProcess(CurrentCPU))
|
|
goto Success;
|
|
else
|
|
goto Idle;
|
|
}
|
|
else
|
|
{
|
|
// Save current process and thread registries, gs, fs, fpu, etc...
|
|
CurrentCPU->CurrentThread->Registers = *Frame;
|
|
CPU::x64::fxsave(CurrentCPU->CurrentThread->FXRegion);
|
|
CurrentCPU->CurrentThread->GSBase = CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE);
|
|
CurrentCPU->CurrentThread->FSBase = CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE);
|
|
|
|
// Set the process & thread as ready if it's running.
|
|
if (CurrentCPU->CurrentProcess->Status == TaskStatus::Running)
|
|
CurrentCPU->CurrentProcess->Status = TaskStatus::Ready;
|
|
if (CurrentCPU->CurrentThread->Status == TaskStatus::Running)
|
|
CurrentCPU->CurrentThread->Status = TaskStatus::Ready;
|
|
|
|
// Get next available thread from the list.
|
|
if (this->GetNextAvailableThread(CurrentCPU))
|
|
goto Success;
|
|
|
|
// If the last process didn't find a thread to execute, we search for a new process.
|
|
if (this->GetNextAvailableProcess(CurrentCPU))
|
|
goto Success;
|
|
|
|
// Before checking from the beginning, we remove everything that is terminated.
|
|
this->SchedulerCleanupProcesses();
|
|
|
|
// If we didn't find anything, we check from the start of the list. This is the last chance to find something or we go to idle.
|
|
if (SchedulerSearchProcessThread(CurrentCPU))
|
|
goto Success;
|
|
else
|
|
goto Idle;
|
|
}
|
|
goto UnwantedReach; // This should never happen.
|
|
|
|
Idle:
|
|
{
|
|
CurrentCPU->CurrentProcess = IdleProcess;
|
|
CurrentCPU->CurrentThread = IdleThread;
|
|
goto Success;
|
|
}
|
|
|
|
Success:
|
|
{
|
|
#ifdef DEBUG_SCHEDULER
|
|
static int sanity;
|
|
const char *Statuses[] = {
|
|
"FF0000", // Unknown
|
|
"AAFF00", // Ready
|
|
"00AA00", // Running
|
|
"FFAA00", // Sleeping
|
|
"FFAA00", // Waiting
|
|
"FF0088", // Stopped
|
|
"FF0000", // Terminated
|
|
};
|
|
const char *StatusesSign[] = {
|
|
"U", // Unknown
|
|
"R", // Ready
|
|
"r", // Running
|
|
"S", // Sleeping
|
|
"W", // Waiting
|
|
"s", // Stopped
|
|
"T", // Terminated
|
|
};
|
|
for (int i = 0; i < 200; i++)
|
|
for (int j = 0; j < 200; j++)
|
|
Display->SetPixel(i, j, 0x222222, 0);
|
|
uint32_t tmpX, tmpY;
|
|
Display->GetBufferCursor(0, &tmpX, &tmpY);
|
|
Display->SetBufferCursor(0, 0, 0);
|
|
foreach (auto var in ListProcess)
|
|
{
|
|
int statuu = var->Status;
|
|
printf_("\e%s-> \eAABBCC%s\eCCCCCC[%d] \e00AAAA%s\n",
|
|
Statuses[statuu], var->Name, statuu, StatusesSign[statuu]);
|
|
foreach (auto var2 in var->Threads)
|
|
{
|
|
int statui = var2->Status;
|
|
printf_(" \e%s-> \eAABBCC%s\eCCCCCC[%d] \e00AAAA%s\n\eAABBCC",
|
|
Statuses[statui], var2->Name, statui, StatusesSign[statui]);
|
|
}
|
|
}
|
|
printf_("%d", sanity++);
|
|
if (sanity > 1000)
|
|
sanity = 0;
|
|
Display->SetBufferCursor(0, tmpX, tmpY);
|
|
Display->SetBuffer(0);
|
|
#endif
|
|
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;
|
|
|
|
// This should never happen, but if it does, we can fix it.
|
|
if (CurrentCPU->CurrentThread->Security.TrustLevel == TaskTrustLevel::User)
|
|
{
|
|
if (CurrentCPU->CurrentThread->Registers.cs != GDT_USER_CODE ||
|
|
CurrentCPU->CurrentThread->Registers.ss != GDT_USER_DATA)
|
|
{
|
|
warn("Wrong CS or SS for user thread %s(%ld)! (Code:%#lx, Data:%#lx != Code:%#lx, Data:%#lx)",
|
|
CurrentCPU->CurrentThread->Registers.cs, CurrentCPU->CurrentThread->Registers.ss,
|
|
GDT_USER_CODE, GDT_USER_DATA,
|
|
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID);
|
|
CurrentCPU->CurrentThread->Registers.cs = GDT_USER_CODE;
|
|
CurrentCPU->CurrentThread->Registers.ss = GDT_USER_DATA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CurrentCPU->CurrentThread->Registers.cs != GDT_KERNEL_CODE ||
|
|
CurrentCPU->CurrentThread->Registers.ss != GDT_KERNEL_DATA)
|
|
{
|
|
warn("Wrong CS or SS for kernel thread %s(%ld)! (Code:%#lx, Data:%#lx != Code:%#lx, Data:%#lx",
|
|
CurrentCPU->CurrentThread->Registers.cs, CurrentCPU->CurrentThread->Registers.ss,
|
|
GDT_KERNEL_CODE, GDT_KERNEL_DATA,
|
|
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID);
|
|
CurrentCPU->CurrentThread->Registers.cs = GDT_KERNEL_CODE;
|
|
CurrentCPU->CurrentThread->Registers.ss = GDT_KERNEL_DATA;
|
|
}
|
|
}
|
|
*Frame = CurrentCPU->CurrentThread->Registers;
|
|
GlobalDescriptorTable::SetKernelStack((void *)((uint64_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->FXRegion);
|
|
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase);
|
|
CPU::x64::wrmsr(CPU::x64::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase);
|
|
|
|
switch (CurrentCPU->CurrentProcess->Security.TrustLevel)
|
|
{
|
|
case TaskTrustLevel::System:
|
|
case TaskTrustLevel::Idle:
|
|
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;
|
|
}
|
|
goto End;
|
|
}
|
|
UnwantedReach:
|
|
{
|
|
warn("Unwanted reach!");
|
|
OneShot(100);
|
|
goto 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);
|
|
OneShot(CurrentCPU->CurrentThread->Info.Priority);
|
|
}
|
|
{
|
|
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:
|
|
{
|
|
__sync_synchronize(); // TODO: Is this really needed?
|
|
}
|
|
}
|
|
|
|
SafeFunction __no_instrument_function void Task::OnInterruptReceived(CPU::x64::TrapFrame *Frame) { this->Schedule(Frame); }
|
|
#elif defined(__i386__)
|
|
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); }
|
|
#elif defined(__aarch64__)
|
|
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
|
|
|
|
void ThreadDoExit()
|
|
{
|
|
// TODO: How I can lock the scheduler without causing a deadlock?
|
|
CPUData *CPUData = GetCurrentCPU();
|
|
CPUData->CurrentThread->Status = TaskStatus::Terminated;
|
|
debug("\"%s\"(%d) exited with code: %#lx", CPUData->CurrentThread->Name, CPUData->CurrentThread->ID, CPUData->CurrentThread->ExitCode);
|
|
CPU::Halt(true);
|
|
}
|
|
|
|
PCB *Task::GetCurrentProcess() { return GetCurrentCPU()->CurrentProcess; }
|
|
TCB *Task::GetCurrentThread() { return GetCurrentCPU()->CurrentThread; }
|
|
|
|
void Task::WaitForProcess(PCB *pcb)
|
|
{
|
|
if (!pcb)
|
|
return;
|
|
if (pcb->Status == TaskStatus::UnknownStatus)
|
|
return;
|
|
debug("Waiting for process \"%s\"(%d)", pcb->Name, pcb->ID);
|
|
while (pcb->Status != TaskStatus::Terminated)
|
|
CPU::Halt();
|
|
}
|
|
|
|
void Task::WaitForThread(TCB *tcb)
|
|
{
|
|
if (!tcb)
|
|
return;
|
|
if (tcb->Status == TaskStatus::UnknownStatus)
|
|
return;
|
|
debug("Waiting for thread \"%s\"(%d)", tcb->Name, tcb->ID);
|
|
while (tcb->Status != TaskStatus::Terminated)
|
|
CPU::Halt();
|
|
}
|
|
|
|
TCB *Task::CreateThread(PCB *Parent,
|
|
IP EntryPoint,
|
|
const char **argv,
|
|
const char **envp,
|
|
Vector<AuxiliaryVector> &auxv,
|
|
IPOffset Offset,
|
|
TaskArchitecture Architecture,
|
|
TaskCompatibility Compatibility)
|
|
{
|
|
SmartCriticalSection(TaskingLock);
|
|
TCB *Thread = new TCB;
|
|
if (Parent == nullptr)
|
|
{
|
|
Thread->Parent = this->GetCurrentProcess();
|
|
if (Thread->Parent == nullptr)
|
|
{
|
|
error("Failed to get current process. Thread cannot be created.");
|
|
delete Thread;
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
Thread->Parent = Parent;
|
|
|
|
if (!Parent)
|
|
{
|
|
error("Parent is null");
|
|
delete Thread;
|
|
return nullptr;
|
|
}
|
|
|
|
Thread->ID = this->NextTID++;
|
|
strcpy(Thread->Name, Parent->Name);
|
|
Thread->EntryPoint = EntryPoint;
|
|
Thread->Offset = Offset;
|
|
Thread->ExitCode = 0xdead;
|
|
Thread->Status = TaskStatus::Ready;
|
|
|
|
#if defined(__amd64__)
|
|
memset(&Thread->Registers, 0, sizeof(CPU::x64::TrapFrame)); // Just in case
|
|
Thread->Registers.rip = (EntryPoint + Offset);
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
switch (Parent->Security.TrustLevel)
|
|
{
|
|
case TaskTrustLevel::System:
|
|
warn("Trust level not supported.");
|
|
[[fallthrough]];
|
|
case TaskTrustLevel::Idle:
|
|
case TaskTrustLevel::Kernel:
|
|
{
|
|
Thread->Stack = new Memory::StackGuard(false, Parent->PageTable);
|
|
#if defined(__amd64__)
|
|
SecurityManager.TrustToken(Thread->Security.UniqueToken, TokenTrustLevel::TrustedByKernel);
|
|
Thread->GSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_GS_BASE);
|
|
Thread->FSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_FS_BASE);
|
|
Thread->Registers.cs = GDT_KERNEL_CODE;
|
|
Thread->Registers.ss = GDT_KERNEL_DATA;
|
|
Thread->Registers.rflags.AlwaysOne = 1;
|
|
Thread->Registers.rflags.IF = 1;
|
|
Thread->Registers.rflags.ID = 1;
|
|
Thread->Registers.rsp = ((uint64_t)Thread->Stack->GetStackTop());
|
|
POKE(uint64_t, Thread->Registers.rsp) = (uint64_t)ThreadDoExit;
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
break;
|
|
}
|
|
case TaskTrustLevel::User:
|
|
{
|
|
Thread->Stack = new Memory::StackGuard(true, Parent->PageTable);
|
|
#if defined(__amd64__)
|
|
SecurityManager.TrustToken(Thread->Security.UniqueToken, TokenTrustLevel::Untrusted);
|
|
Thread->GSBase = 0;
|
|
Thread->FSBase = 0;
|
|
Thread->Registers.cs = GDT_USER_CODE;
|
|
Thread->Registers.ss = GDT_USER_DATA;
|
|
Thread->Registers.rflags.AlwaysOne = 1;
|
|
// Thread->Registers.rflags.PF = 1;
|
|
// Thread->Registers.rflags.SF = 1;
|
|
// Thread->Registers.rflags.IOPL = 3;
|
|
Thread->Registers.rflags.IF = 1;
|
|
Thread->Registers.rflags.ID = 1;
|
|
Thread->Registers.rsp = ((uint64_t)Thread->Stack->GetStackTop());
|
|
|
|
if (Compatibility == TaskCompatibility::Linux)
|
|
{
|
|
// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf#figure.3.9
|
|
// What is a "eightbyte"? unsigned long? 1 eightbyte = 8 bytes? 2 eightbyte each = 16 bytes?
|
|
uint64_t TmpStack = Thread->Registers.rsp;
|
|
uint64_t TmpStack2 = TmpStack;
|
|
uint64_t *TmpStackPtr = (uint64_t *)TmpStack;
|
|
|
|
// TODO: argc, argv, envp, auxv not tested and probably not working
|
|
// foreach (auto var in envp)
|
|
// {
|
|
// TmpStack -= strlen(var) + 1;
|
|
// strcpy((char *)TmpStack, var);
|
|
// }
|
|
|
|
// foreach (auto var in argv)
|
|
// {
|
|
// TmpStack -= strlen(var) + 1;
|
|
// strcpy((char *)TmpStack, var);
|
|
// }
|
|
|
|
/* align by 16 */
|
|
TmpStack = (uint64_t)((uint64_t)TmpStack - ((uint64_t)TmpStack & 0x0F));
|
|
|
|
/* TODO: more alignment here? */
|
|
|
|
/* auxv null */
|
|
TmpStack -= sizeof(uint64_t);
|
|
POKE(uint64_t, TmpStack) = (uint64_t)0;
|
|
/* This should be included too? */
|
|
TmpStack -= sizeof(uint64_t);
|
|
POKE(uint64_t, TmpStack) = (uint64_t)0;
|
|
|
|
/* auxv */
|
|
foreach (auto var in auxv)
|
|
{
|
|
if (var.archaux.a_type == AT_ENTRY)
|
|
Thread->Registers.rdi = var.archaux.a_un.a_val;
|
|
|
|
TmpStack -= sizeof(uint64_t) * 2;
|
|
POKE(uint64_t, TmpStack) = (uint64_t)var.archaux.a_type;
|
|
TmpStack -= sizeof(uint64_t) * 2;
|
|
POKE(uint64_t, TmpStack) = (uint64_t)var.archaux.a_un.a_val;
|
|
}
|
|
|
|
/* empty */
|
|
TmpStack -= sizeof(uint64_t);
|
|
POKE(uint64_t, TmpStack) = 0;
|
|
|
|
/* envp pointers */
|
|
// for (uint64_t i = 0; i < envp.size(); i++)
|
|
// {
|
|
// /* Not sure if this works */
|
|
// TmpStack2 -= strlen(envp[i]) + 1;
|
|
// TmpStackPtr[i] = TmpStack2;
|
|
// }
|
|
|
|
/* empty */
|
|
TmpStack -= sizeof(uint64_t);
|
|
POKE(uint64_t, TmpStack) = 0;
|
|
|
|
/* argv pointers */
|
|
// for (uint64_t i = 0; i < argv.size(); i++)
|
|
// {
|
|
// /* Not sure if this works */
|
|
// TmpStack2 -= strlen(argv[i]) + 1;
|
|
// TmpStackPtr[i] = TmpStack2;
|
|
// }
|
|
|
|
/* argc */
|
|
TmpStack -= sizeof(uint64_t);
|
|
// POKE(uint64_t, TmpStack) = argv.size() - 1;
|
|
|
|
Thread->Registers.rsp -= (uint64_t)Thread->Stack->GetStackTop() - TmpStack;
|
|
}
|
|
else // Native
|
|
{
|
|
uint64_t ArgvSize = 0;
|
|
uint64_t ArgvStrSize = 0;
|
|
if (argv)
|
|
{
|
|
while (argv[ArgvSize] != nullptr)
|
|
{
|
|
debug("> ArgvSize: %d, ArgvStrSize: %d", ArgvSize, ArgvStrSize);
|
|
ArgvSize++;
|
|
ArgvStrSize += strlen(argv[ArgvSize]) + 1;
|
|
debug("< ArgvSize: %d, ArgvStrSize: %d", ArgvSize, ArgvStrSize);
|
|
}
|
|
}
|
|
|
|
uint64_t EnvpSize = 0;
|
|
uint64_t EnvpStrSize = 0;
|
|
if (envp)
|
|
{
|
|
while (envp[EnvpSize] != nullptr)
|
|
{
|
|
debug("> EnvpSize: %d, EnvpStrSize: %d", EnvpSize, EnvpStrSize);
|
|
EnvpSize++;
|
|
EnvpStrSize += strlen(envp[EnvpSize]) + 1;
|
|
debug("< EnvpSize: %d, EnvpStrSize: %d", EnvpSize, EnvpStrSize);
|
|
}
|
|
}
|
|
|
|
uint8_t *_argv = 0;
|
|
uint8_t *_envp = 0;
|
|
|
|
for (uint64_t i = 0; i < ArgvSize; i++)
|
|
{
|
|
void *Tmp = KernelAllocator.RequestPages(TO_PAGES(strlen(argv[i]) + 1));
|
|
debug("argv[%d] ptr %#lx", i, (uint64_t)Tmp);
|
|
Memory::Virtual().Map(Tmp, Tmp, Memory::PTFlag::RW | Memory::PTFlag::US);
|
|
_argv = (uint8_t *)Tmp;
|
|
strcpy((char *)_argv, argv[i]);
|
|
argv[i] = (char *)_argv;
|
|
}
|
|
|
|
debug("argv done");
|
|
|
|
for (uint64_t i = 0; i < EnvpSize; i++)
|
|
{
|
|
void *Tmp = KernelAllocator.RequestPages(TO_PAGES(strlen(envp[i]) + 1));
|
|
debug("envp[%d] ptr %#lx", i, (uint64_t)Tmp);
|
|
Memory::Virtual().Map(Tmp, Tmp, Memory::PTFlag::RW | Memory::PTFlag::US);
|
|
_envp = (uint8_t *)Tmp;
|
|
strcpy((char *)_envp, envp[i]);
|
|
envp[i] = (char *)_envp;
|
|
}
|
|
|
|
debug("envp done");
|
|
|
|
Thread->Registers.rdi = ArgvSize;
|
|
Thread->Registers.rsi = (uint64_t)_argv;
|
|
Thread->Registers.rdx = (uint64_t)_envp;
|
|
|
|
for (uint64_t i = 0; i < ArgvSize; i++)
|
|
debug("argv[%d]: %s", i, _argv[i]);
|
|
for (uint64_t i = 0; i < EnvpSize; i++)
|
|
debug("envp[%d]: %s", i, _envp[i]);
|
|
}
|
|
|
|
/* We need to leave the libc's crt to make a syscall when the Thread is exited or we are going to get GPF or PF exception. */
|
|
|
|
Memory::Virtual uva = Memory::Virtual(Parent->PageTable);
|
|
if (!uva.Check((void *)Offset, Memory::PTFlag::US))
|
|
{
|
|
error("Offset is not user accessible");
|
|
uva.Map((void *)Offset, (void *)Offset, Memory::PTFlag::RW | Memory::PTFlag::US); // We try one more time.
|
|
}
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
#ifdef DEBUG_SCHEDULER
|
|
DumpData(Thread->Name, Thread->Stack, STACK_SIZE);
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
error("Unknown elevation.");
|
|
delete Thread->Stack;
|
|
this->NextTID--;
|
|
delete Thread;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Thread->Security.TrustLevel = Parent->Security.TrustLevel;
|
|
// Thread->Security.UniqueToken = SecurityManager.CreateToken();
|
|
|
|
Thread->Info = {};
|
|
Thread->Info.SpawnTime = CPU::Counter();
|
|
Thread->Info.Year = 0;
|
|
Thread->Info.Month = 0;
|
|
Thread->Info.Day = 0;
|
|
Thread->Info.Hour = 0;
|
|
Thread->Info.Minute = 0;
|
|
Thread->Info.Second = 0;
|
|
for (int i = 0; i < MAX_CPU; i++)
|
|
{
|
|
Thread->Info.Usage[i] = 0;
|
|
Thread->Info.Affinity[i] = true;
|
|
}
|
|
Thread->Info.Priority = 10;
|
|
Thread->Info.Architecture = Architecture;
|
|
Thread->Info.Compatibility = Compatibility;
|
|
|
|
debug("Thread offset is %#lx (EntryPoint: %#lx) => RIP: %#lx", Thread->Offset, Thread->EntryPoint, Thread->Registers.rip);
|
|
if (Parent->Security.TrustLevel == TaskTrustLevel::User)
|
|
debug("Thread stack region is %#lx-%#lx (U) and rsp is %#lx", Thread->Stack->GetStackBottom(), Thread->Stack->GetStackTop(), Thread->Registers.rsp);
|
|
else
|
|
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx", Thread->Stack->GetStackBottom(), Thread->Stack->GetStackTop(), Thread->Registers.rsp);
|
|
debug("Created thread \"%s\"(%d) in process \"%s\"(%d)",
|
|
Thread->Name, Thread->ID,
|
|
Thread->Parent->Name, Thread->Parent->ID);
|
|
|
|
Parent->Threads.push_back(Thread);
|
|
return Thread;
|
|
}
|
|
|
|
PCB *Task::CreateProcess(PCB *Parent,
|
|
const char *Name,
|
|
TaskTrustLevel TrustLevel)
|
|
{
|
|
SmartCriticalSection(TaskingLock);
|
|
PCB *Process = new PCB;
|
|
Process->ID = this->NextPID++;
|
|
strcpy(Process->Name, Name);
|
|
if (Parent == nullptr)
|
|
Process->Parent = this->GetCurrentProcess();
|
|
else
|
|
Process->Parent = Parent;
|
|
Process->ExitCode = 0xdead;
|
|
Process->Status = TaskStatus::Ready;
|
|
|
|
Process->Security.TrustLevel = TrustLevel;
|
|
// Process->Security.UniqueToken = SecurityManager.CreateToken();
|
|
|
|
Process->IPCHandles = new HashMap<InterProcessCommunication::IPCPort, uint64_t>;
|
|
|
|
switch (TrustLevel)
|
|
{
|
|
case TaskTrustLevel::System:
|
|
warn("Trust level not supported.");
|
|
[[fallthrough]];
|
|
case TaskTrustLevel::Idle:
|
|
case TaskTrustLevel::Kernel:
|
|
{
|
|
SecurityManager.TrustToken(Process->Security.UniqueToken, TokenTrustLevel::TrustedByKernel);
|
|
#if defined(__amd64__)
|
|
Process->PageTable = (Memory::PageTable4 *)CPU::x64::readcr3().raw;
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
break;
|
|
}
|
|
case TaskTrustLevel::User:
|
|
{
|
|
SecurityManager.TrustToken(Process->Security.UniqueToken, TokenTrustLevel::Untrusted);
|
|
#if defined(__amd64__)
|
|
Process->PageTable = (Memory::PageTable4 *)KernelAllocator.RequestPages(TO_PAGES(PAGE_SIZE));
|
|
memcpy(Process->PageTable, (void *)UserspaceKernelOnlyPageTable, PAGE_SIZE);
|
|
for (uint64_t i = 0; i < TO_PAGES(PAGE_SIZE); i++)
|
|
Memory::Virtual(Process->PageTable).Map((void *)Process->PageTable, (void *)Process->PageTable, Memory::PTFlag::RW); // Make sure the page table is mapped.
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
error("Unknown elevation.");
|
|
this->NextPID--;
|
|
delete Process;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Process->Info = {};
|
|
Process->Info.SpawnTime = CPU::Counter();
|
|
Process->Info.Year = 0;
|
|
Process->Info.Month = 0;
|
|
Process->Info.Day = 0;
|
|
Process->Info.Hour = 0;
|
|
Process->Info.Minute = 0;
|
|
Process->Info.Second = 0;
|
|
for (int i = 0; i < MAX_CPU; i++)
|
|
{
|
|
Process->Info.Usage[i] = 0;
|
|
Process->Info.Affinity[i] = true;
|
|
}
|
|
Process->Info.Priority = 10;
|
|
|
|
debug("Process page table: %#lx", Process->PageTable);
|
|
debug("Created process \"%s\"(%d) in process \"%s\"(%d)",
|
|
Process->Name, Process->ID,
|
|
Parent ? Process->Parent->Name : "None",
|
|
Parent ? Process->Parent->ID : 0);
|
|
|
|
if (Parent)
|
|
Parent->Children.push_back(Process);
|
|
ListProcess.push_back(Process);
|
|
return Process;
|
|
}
|
|
|
|
Task::Task(const IP EntryPoint) : Interrupts::Handler(CPU::x64::IRQ16)
|
|
{
|
|
SmartCriticalSection(TaskingLock);
|
|
#if defined(__amd64__)
|
|
for (int i = 0; i < SMP::CPUCores; i++)
|
|
((APIC::APIC *)Interrupts::apic[i])->RedirectIRQ(i, CPU::x64::IRQ16 - CPU::x64::IRQ0, 1);
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
KPrint("Starting Tasking With Instruction Pointer: %p (\e666666%s\eCCCCCC)", EntryPoint, KernelSymbolTable->GetSymbolFromAddress(EntryPoint));
|
|
TaskingLock.Unlock();
|
|
|
|
#if defined(__amd64__)
|
|
TaskArchitecture Arch = TaskArchitecture::x64;
|
|
#elif defined(__i386__)
|
|
TaskArchitecture Arch = TaskArchitecture::x32;
|
|
#elif defined(__aarch64__)
|
|
TaskArchitecture Arch = TaskArchitecture::ARM64;
|
|
#endif
|
|
PCB *kproc = CreateProcess(nullptr, "Kernel", TaskTrustLevel::Kernel);
|
|
Vector<AuxiliaryVector> auxv;
|
|
TCB *kthrd = CreateThread(kproc, EntryPoint, nullptr, nullptr, auxv, 0, Arch);
|
|
kthrd->Rename("Main Thread");
|
|
debug("Created Kernel Process: %s and Thread: %s", kproc->Name, kthrd->Name);
|
|
TaskingLock.Lock(__FUNCTION__);
|
|
|
|
#if defined(__amd64__)
|
|
uint32_t rax, rbx, rcx, rdx;
|
|
CPU::x64::cpuid(0x1, &rax, &rbx, &rcx, &rdx);
|
|
if (rcx & CPU::x64::CPUID_FEAT_RCX_MONITOR)
|
|
{
|
|
trace("CPU has MONITOR/MWAIT support.");
|
|
}
|
|
|
|
if (!CPU::Interrupts(CPU::Check))
|
|
{
|
|
error("Interrupts are not enabled.");
|
|
CPU::Interrupts(CPU::Enable);
|
|
}
|
|
#endif
|
|
TaskingLock.Unlock();
|
|
IdleProcess = CreateProcess(nullptr, (char *)"Idle", TaskTrustLevel::Idle);
|
|
for (int i = 0; i < SMP::CPUCores; i++)
|
|
{
|
|
Vector<AuxiliaryVector> auxv;
|
|
IdleThread = CreateThread(IdleProcess, reinterpret_cast<uint64_t>(IdleProcessLoop), nullptr, nullptr, auxv);
|
|
char IdleName[16];
|
|
sprintf_(IdleName, "Idle Thread %d", i);
|
|
IdleThread->Rename(IdleName);
|
|
IdleThread->SetPriority(1);
|
|
break;
|
|
}
|
|
debug("Tasking Started");
|
|
#if defined(__amd64__)
|
|
((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x64::IRQ16, 100);
|
|
|
|
for (int i = 1; i < SMP::CPUCores; i++)
|
|
{
|
|
// ((APIC::Timer *)Interrupts::apicTimer[i])->OneShot(CPU::x64::IRQ16, 100);
|
|
// TODO: Lock was the fault here. Now crash handler should support SMP.
|
|
// APIC::InterruptCommandRegisterLow icr;
|
|
// icr.Vector = CPU::x64::IRQ16;
|
|
// icr.Level = APIC::APICLevel::Assert;
|
|
// ((APIC::APIC *)Interrupts::apic[0])->IPI(i, icr);
|
|
}
|
|
#elif defined(__i386__)
|
|
#elif defined(__aarch64__)
|
|
#endif
|
|
}
|
|
|
|
Task::~Task()
|
|
{
|
|
SmartCriticalSection(TaskingLock);
|
|
trace("Stopping tasking");
|
|
}
|
|
}
|