Merge remote-tracking branch 'Kernel/mb2_32_64_test' into Kernel-mb2_32_64_test

This commit is contained in:
EnderIce2
2024-11-20 05:15:06 +02:00
parent 47cf2c24d1
commit b348932172
353 changed files with 77068 additions and 0 deletions

View File

@@ -0,0 +1,259 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ipc.hpp>
#include <task.hpp>
#include "../kernel.h"
#include "../ipc.h"
namespace InterProcessCommunication
{
IPCHandle *IPC::Create(IPCType Type, char UniqueToken[16])
{
SmartLock(this->IPCLock);
IPCHandle *Hnd = (IPCHandle *)mem->RequestPages(TO_PAGES(sizeof(IPCHandle) + 1));
Hnd->ID = NextID++;
Hnd->Node = vfs->Create(UniqueToken, VirtualFileSystem::NodeFlags::FILE, IPCNode);
Hnd->Buffer = nullptr;
Hnd->Length = 0;
Hnd->Listening = false;
Handles.push_back(Hnd);
debug("Created IPC with ID %d", Hnd->ID);
return Hnd;
}
IPCErrorCode IPC::Destroy(IPCID ID)
{
SmartLock(this->IPCLock);
for (size_t i = 0; i < Handles.size(); i++)
{
if (Handles[i]->ID == ID)
{
vfs->Delete(Handles[i]->Node);
mem->FreePages(Handles[i], TO_PAGES(sizeof(IPCHandle) + 1));
Handles.remove(i);
debug("Destroyed IPC with ID %d", ID);
return IPCSuccess;
}
}
debug("Failed to destroy IPC with ID %d", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Allocate(IPCID ID, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
return IPCError;
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Buffer != nullptr || Hnd->Length != 0)
return IPCAlreadyAllocated;
Hnd->Buffer = (uint8_t *)mem->RequestPages(TO_PAGES(Size + 1));
Hnd->Length = Size;
return IPCSuccess;
}
}
return IPCIDNotFound;
}
IPCErrorCode IPC::Deallocate(IPCID ID)
{
SmartLock(this->IPCLock);
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Buffer == nullptr || Hnd->Length == 0)
return IPCNotAllocated;
mem->FreePages(Hnd->Buffer, TO_PAGES(Hnd->Length + 1));
Hnd->Buffer = nullptr;
Hnd->Length = 0;
return IPCSuccess;
}
}
return IPCIDNotFound;
}
IPCErrorCode IPC::Read(IPCID ID, void *Buffer, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
return IPCError;
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (Hnd->Listening)
{
debug("IPC %d is listening", ID);
return IPCNotListening;
}
if (Hnd->Length < Size)
{
debug("IPC %d is too small", ID);
return IPCError;
}
debug("IPC %d reading %d bytes", ID, Size);
memcpy(Buffer, Hnd->Buffer, Size);
debug("IPC read %d bytes", Size);
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Write(IPCID ID, void *Buffer, long Size)
{
SmartLock(this->IPCLock);
if (Size < 0)
{
debug("IPC %d is too small", ID);
return IPCError;
}
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (!Hnd->Listening)
{
debug("IPC %d is NOT listening", ID);
return IPCNotListening;
}
if (Hnd->Length < Size)
{
debug("IPC %d is too small", ID);
return IPCError;
}
debug("IPC %d writing %d bytes", ID, Size);
memcpy(Hnd->Buffer, Buffer, Size);
Hnd->Listening = false;
debug("IPC %d wrote %d bytes and now is %s", ID, Size, Hnd->Listening ? "listening" : "ready");
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Listen(IPCID ID, bool Listen)
{
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
Hnd->Listening = Listen;
debug("IPC %d is now set to %s", ID, Listen ? "listening" : "ready");
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCErrorCode IPC::Wait(IPCID ID)
{
foreach (auto Hnd in Handles)
{
if (Hnd->ID == ID)
{
if (!CPU::Interrupts())
warn("Interrupts are disabled. This may cause a kernel hang.");
debug("Waiting for IPC %d (now %s)", ID, Hnd->Listening ? "listening" : "ready");
while (Hnd->Listening)
CPU::Pause();
debug("IPC %d is ready", ID);
return IPCSuccess;
}
}
debug("IPC %d not found", ID);
return IPCIDNotFound;
}
IPCHandle *IPC::SearchByToken(char UniqueToken[16])
{
foreach (auto Hnd in Handles)
{
if (strcmp(Hnd->Node->Name, UniqueToken) == 0)
{
debug("Found IPC with token %s", UniqueToken);
return Hnd;
}
}
debug("Failed to find IPC with token %s", UniqueToken);
return nullptr;
}
int IPC::HandleSyscall(long Command, long Type, int ID, int Flags, void *Buffer, size_t Size)
{
switch (Command)
{
case IPC_CREATE:
{
char UniqueToken[16];
if (Buffer != nullptr)
strcpy(UniqueToken, (char *)Buffer);
else
snprintf(UniqueToken, 16, "IPC_%d", ID);
IPCHandle *Hnd = this->Create((IPCType)Type, UniqueToken);
this->Allocate(Hnd->ID, Size ? Size : PAGE_SIZE);
return Hnd->ID;
}
case IPC_READ:
return this->Read(ID, Buffer, Size);
case IPC_WRITE:
return TaskManager->GetProcessByID(Flags)->IPC->Write(ID, Buffer, Size);
case IPC_DELETE:
{
this->Deallocate(ID);
return this->Destroy(ID);
}
case IPC_WAIT:
return this->Wait(ID);
case IPC_LISTEN:
return this->Listen(ID, Flags);
default:
return IPCInvalidCommand;
}
return IPCError;
}
IPC::IPC(void *Process)
{
this->Process = Process;
mem = new Memory::MemMgr(nullptr, ((Tasking::PCB *)Process)->memDirectory);
IPCNode = vfs->Create("ipc", VirtualFileSystem::NodeFlags::DIRECTORY, ((Tasking::PCB *)this->Process)->ProcessDirectory);
}
IPC::~IPC()
{
delete mem, mem = nullptr;
vfs->Delete(IPCNode, true);
}
}

View File

@@ -0,0 +1,727 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../Architecture/amd64/cpu/apic.hpp"
#include "../Architecture/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../Architecture/i386/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", ProcessList.size());
#ifdef DEBUG_FIND_NEW_PROCESS
foreach (auto process in ProcessList)
fnp_schedbg("Process %d %s", process->ID, process->Name);
#endif
foreach (auto process in ProcessList)
{
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(), ProcessList.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 ProcessList)
{
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(), ProcessList.size());
return true;
}
}
gnap_schedbg("No process to run.");
return false;
}
SafeFunction NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
{
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
foreach (auto process in ProcessList)
{
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(), ProcessList.size());
return true;
}
}
return false;
}
SafeFunction NIF void Task::UpdateProcessStatus()
{
foreach (auto process in ProcessList)
{
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()
{
foreach (auto process in ProcessList)
{
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;
}
bool ProcessNotChanged = false;
CPU::x64::writecr3({.raw = (uint64_t)KernelPageTable}); /* Restore kernel page table for safety reasons. */
uint64_t SchedTmpTicks = TimeManager->GetCounter();
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.");
ProcessNotChanged = true;
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();
schedbg("Passed WakeUpThreads");
if (this->GetNextAvailableThread(CurrentCPU))
{
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
SuccessSource = 1;
#endif
ProcessNotChanged = true;
goto Success;
}
schedbg("Passed GetNextAvailableThread");
if (this->GetNextAvailableProcess(CurrentCPU))
{
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
SuccessSource = 2;
#endif
goto Success;
}
schedbg("Passed GetNextAvailableProcess");
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;
}
}
warn("Unwanted reach!");
TaskingScheduler_OneShot(100);
goto End;
Idle:
ProcessNotChanged = true;
CurrentCPU->CurrentProcess = IdleProcess;
CurrentCPU->CurrentThread = IdleThread;
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);
if (!ProcessNotChanged)
UpdateUsage(&CurrentCPU->CurrentProcess->Info, &CurrentCPU->CurrentProcess->Security, CurrentCPU->ID);
UpdateUsage(&CurrentCPU->CurrentThread->Info, &CurrentCPU->CurrentThread->Security, 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;
}
if (!ProcessNotChanged)
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
(&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter();
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("================================================================");
End:
this->SchedulerTicks.store(TimeManager->GetCounter() - 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 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 bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
{
fixme("unimplemented");
}
SafeFunction void Task::Schedule(CPU::aarch64::TrapFrame *Frame)
{
fixme("unimplemented");
}
SafeFunction void Task::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) { this->Schedule(Frame); }
#endif
}

149
Kernel/Tasking/Security.cpp Normal file
View File

@@ -0,0 +1,149 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <rand.hpp>
#include <debug.h>
#include <vector>
namespace Tasking
{
Token Security::CreateToken()
{
uint64_t ret = 0;
Retry:
ret = Random::rand64();
foreach (auto t in Tokens)
if (t.token == ret)
goto Retry;
Tokens.push_back({ret, UnknownTrustLevel, 0, false});
debug("Created token %#lx", ret);
return ret;
}
bool Security::TrustToken(Token token, TTL TrustLevel)
{
foreach (auto &t in Tokens)
{
if (t.token == token)
{
t.TrustLevel = TrustLevel;
debug("Trusted token %#lx to level %d", token, t.TrustLevel);
return true;
}
}
warn("Failed to trust token %#lx", token);
return false;
}
bool Security::UntrustToken(Token token)
{
foreach (auto &t in Tokens)
{
if (t.token == token)
{
t.TrustLevel = Untrusted;
debug("Untrusted token %#lx", token);
return true;
}
}
warn("Failed to untrust token %#lx", token);
return false;
}
bool Security::AddTrustLevel(Token token, TTL TrustLevel)
{
foreach (auto &t in Tokens)
{
if (t.token == token)
{
t.TrustLevel |= TrustLevel;
debug("Added trust level %d to token %#lx", t.TrustLevel, token);
return true;
}
}
warn("Failed to add trust level %d to token %#lx", TrustLevel, token);
return false;
}
bool Security::RemoveTrustLevel(Token token, TTL TrustLevel)
{
foreach (auto &t in Tokens)
{
if (t.token == token)
{
t.TrustLevel &= ~TrustLevel;
debug("Removed trust level %d from token %#lx", t.TrustLevel, token);
return true;
}
}
warn("Failed to remove trust level %d from token %#lx", TrustLevel, token);
return false;
}
bool Security::DestroyToken(Token token)
{
fixme("DestroyToken->true");
UNUSED(token);
return true;
}
bool Security::IsTokenTrusted(Token token, TTL TrustLevel)
{
foreach (auto t in Tokens)
if (t.token == token)
{
if (t.TrustLevel == TrustLevel)
return true;
else
return false;
}
warn("Failed to check trust level of token %#lx", token);
return false;
}
bool Security::IsTokenTrusted(Token token, int TrustLevel)
{
foreach (auto t in Tokens)
if (t.token == token)
{
if (t.TrustLevel & TrustLevel)
return true;
else
return false;
}
warn("Failed to check trust level of token %#lx", token);
return false;
}
int Security::GetTokenTrustLevel(Token token)
{
foreach (auto t in Tokens)
if (t.token == token)
return t.TrustLevel;
warn("Failed to get trust level of token %#lx", token);
return UnknownTrustLevel;
}
Security::Security() {}
Security::~Security() { Tokens.clear(); }
}

874
Kernel/Tasking/Task.cpp Normal file
View File

@@ -0,0 +1,874 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <task.hpp>
#include <dumper.hpp>
#include <convert.h>
#include <lock.hpp>
#include <printf.h>
#include <smp.hpp>
#include <io.h>
#include "../kernel.h"
#if defined(a64)
#include "../Architecture/amd64/cpu/apic.hpp"
#include "../Architecture/amd64/cpu/gdt.hpp"
#elif defined(a32)
#include "../Architecture/i386/cpu/apic.hpp"
#elif defined(aa64)
#endif
// #define DEBUG_TASKING 1
#ifdef DEBUG_TASKING
#define tskdbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define tskdbg(m, ...)
#endif
NewLock(TaskingLock);
namespace Tasking
{
void Task::Schedule()
{
if (!StopScheduler)
TaskingScheduler_OneShot(1);
// APIC::InterruptCommandRegisterLow icr;
// icr.Vector = CPU::x86::IRQ16;
// icr.Level = APIC::APICLevel::Assert;
// ((APIC::APIC *)Interrupts::apic[0])->IPI(GetCurrentCPU()->ID, icr);
}
#if defined(a86)
__naked __used __no_stack_protector NIF void IdleProcessLoop()
{
asmv("IdleLoop:\n"
"hlt\n"
"jmp IdleLoop\n");
#elif defined(aa64)
__used __no_stack_protector NIF void IdleProcessLoop()
{
asmv("IdleLoop:\n"
"wfe\n"
"b IdleLoop\n");
#endif
}
SafeFunction NIF bool Task::InvalidPCB(PCB *pcb)
{
if (!pcb)
return true;
if (pcb >= (PCB *)(UINTPTR_MAX - 0x1ffe)) /* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
return true;
if (pcb < (PCB *)(0x1000)) /* In this section of the memory is reserved by the kernel. */
return true;
if (!Memory::Virtual().Check((void *)pcb)) /* Check if it's mapped. */
return true;
return false;
}
SafeFunction NIF bool Task::InvalidTCB(TCB *tcb)
{
if (!tcb)
return true;
if (tcb >= (TCB *)(UINTPTR_MAX - 0x1ffe)) /* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
return true;
if (tcb < (TCB *)(0x1000)) /* In this section of the memory is reserved by the kernel. */
return true;
if (!Memory::Virtual().Check((void *)tcb)) /* Check if it's mapped. */
return true;
return false;
}
SafeFunction NIF void Task::RemoveThread(TCB *Thread)
{
for (size_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, Thread->Stack = nullptr;
delete Thread->Memory, Thread->Memory = nullptr;
SecurityManager.DestroyToken(Thread->Security.UniqueToken);
delete Thread->Parent->Threads[i], Thread->Parent->Threads[i] = nullptr;
// Remove from the list
Thread->Parent->Threads.remove(i);
break;
}
}
SafeFunction NIF 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 (size_t i = 0; i < ProcessList.size(); i++)
{
if (ProcessList[i] == Process)
{
trace("Process \"%s\"(%d) removed from the list", Process->Name, Process->ID);
// Free memory
delete ProcessList[i]->IPC, ProcessList[i]->IPC = nullptr;
delete ProcessList[i]->ELFSymbolTable, ProcessList[i]->ELFSymbolTable = nullptr;
SecurityManager.DestroyToken(ProcessList[i]->Security.UniqueToken);
if (ProcessList[i]->Security.TrustLevel == TaskTrustLevel::User)
KernelAllocator.FreePages((void *)ProcessList[i]->PageTable, TO_PAGES(sizeof(Memory::PageTable4) + 1));
// Remove the process from parent's children list
if (ProcessList[i]->Parent)
for (size_t j = 0; j < ProcessList[i]->Parent->Children.size(); j++)
{
if (ProcessList[i]->Parent->Children[j] == ProcessList[i])
{
ProcessList[i]->Parent->Children.remove(j);
break;
}
}
// Delete process directory
vfs->Delete(ProcessList[i]->ProcessDirectory, true);
// Free memory
delete ProcessList[i], ProcessList[i] = nullptr;
// Remove from the list
ProcessList.remove(i);
break;
}
}
}
else
{
foreach (TCB *thread in Process->Threads)
if (thread->Status == Terminated)
RemoveThread(thread);
}
}
SafeFunction NIF void Task::UpdateUsage(TaskInfo *Info, TaskSecurity *Security, int Core)
{
uint64_t CurrentTime = TimeManager->GetCounter();
uint64_t TimePassed = CurrentTime - Info->LastUpdateTime;
// Info->LastUpdateTime = CurrentTime;
if (Security->TrustLevel == TaskTrustLevel::User)
Info->UserTime += TimePassed;
else
Info->KernelTime += TimePassed;
}
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.load(); }
TCB *Task::GetCurrentThread() { return GetCurrentCPU()->CurrentThread.load(); }
PCB *Task::GetProcessByID(UPID ID)
{
for (size_t i = 0; i < ProcessList.size(); i++)
if (ProcessList[i]->ID == ID)
return ProcessList[i];
return nullptr;
}
TCB *Task::GetThreadByID(UTID ID)
{
for (size_t i = 0; i < ProcessList.size(); i++)
for (size_t j = 0; j < ProcessList[i]->Threads.size(); j++)
if (ProcessList[i]->Threads[j]->ID == ID)
return ProcessList[i]->Threads[j];
return nullptr;
}
void Task::WaitForProcess(PCB *pcb)
{
if (InvalidPCB(pcb))
return;
if (pcb->Status == TaskStatus::UnknownStatus)
return;
debug("Waiting for process \"%s\"(%d)", pcb->Name, pcb->ID);
while (pcb->Status != TaskStatus::Terminated)
CPU::Pause();
}
void Task::WaitForThread(TCB *tcb)
{
if (InvalidTCB(tcb))
return;
if (tcb->Status == TaskStatus::UnknownStatus)
return;
debug("Waiting for thread \"%s\"(%d)", tcb->Name, tcb->ID);
while (tcb->Status != TaskStatus::Terminated)
CPU::Pause();
}
void Task::WaitForProcessStatus(PCB *pcb, TaskStatus status)
{
if (InvalidPCB(pcb))
return;
if (pcb->Status == TaskStatus::UnknownStatus)
return;
debug("Waiting for process \"%s\"(%d) to reach status: %d", pcb->Name, pcb->ID, status);
while (pcb->Status != status)
CPU::Pause();
}
void Task::WaitForThreadStatus(TCB *tcb, TaskStatus status)
{
if (InvalidTCB(tcb))
return;
if (tcb->Status == TaskStatus::UnknownStatus)
return;
debug("Waiting for thread \"%s\"(%d) to reach status: %d", tcb->Name, tcb->ID, status);
while (tcb->Status != status)
CPU::Pause();
}
void Task::Sleep(uint64_t Milliseconds)
{
SmartLock(TaskingLock);
TCB *thread = this->GetCurrentThread();
thread->Status = TaskStatus::Sleeping;
if (thread->Parent->Threads.size() == 1)
thread->Parent->Status = TaskStatus::Sleeping;
thread->Info.SleepUntil = TimeManager->CalculateTarget(Milliseconds, Time::Units::Milliseconds);
tskdbg("Thread \"%s\"(%d) is going to sleep until %llu", thread->Name, thread->ID, thread->Info.SleepUntil);
// TaskingScheduler_OneShot(1);
// IRQ16
TaskingLock.Unlock();
#if defined(a86)
asmv("int $0x30"); /* This will trigger the IRQ16 instantly so we won't execute the next instruction */
#elif defined(aa64)
asmv("svc #0x30"); /* This will trigger the IRQ16 instantly so we won't execute the next instruction */
#endif
}
void Task::SignalShutdown()
{
fixme("SignalShutdown()");
// TODO: Implement this
// This should hang until all processes are terminated
}
void Task::CleanupProcessesThread()
{
while (true)
{
this->Sleep(1000);
foreach (auto process in ProcessList)
{
if (InvalidPCB(process))
continue;
RemoveProcess(process);
}
}
}
void Task::RevertProcessCreation(PCB *Process)
{
for (size_t i = 0; i < ProcessList.size(); i++)
{
if (ProcessList[i] == Process)
{
SecurityManager.DestroyToken(Process->Security.UniqueToken);
if (Process->Security.TrustLevel == TaskTrustLevel::User)
KernelAllocator.FreePages((void *)Process->PageTable, TO_PAGES(sizeof(Memory::PageTable4) + 1));
if (Process->Parent)
for (size_t j = 0; j < Process->Parent->Children.size(); j++)
{
if (Process->Parent->Children[j] == Process)
{
Process->Parent->Children.remove(j);
break;
}
}
delete Process->IPC, Process->IPC = nullptr;
delete Process->ELFSymbolTable, Process->ELFSymbolTable = nullptr;
delete Process, Process = nullptr;
ProcessList.remove(i);
NextPID--;
break;
}
}
}
void Task::RevertThreadCreation(TCB *Thread)
{
for (size_t j = 0; j < Thread->Parent->Threads.size(); j++)
{
if (Thread->Parent->Threads[j] == Thread)
{
Thread->Parent->Threads.remove(j);
break;
}
}
delete Thread->Stack, Thread->Stack = nullptr;
delete Thread->Memory, Thread->Memory = nullptr;
SecurityManager.DestroyToken(Thread->Security.UniqueToken);
delete Thread, Thread = nullptr;
NextTID--;
}
TCB *Task::CreateThread(PCB *Parent,
IP EntryPoint,
const char **argv,
const char **envp,
const std::vector<AuxiliaryVector> &auxv,
IPOffset Offset,
TaskArchitecture Architecture,
TaskCompatibility Compatibility)
{
SmartLock(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 (InvalidPCB(Parent))
{
error("Parent is invalid");
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;
Thread->Memory = new Memory::MemMgr(Parent->PageTable, Parent->memDirectory);
Thread->FPU = (CPU::x64::FXState *)Thread->Memory->RequestPages(TO_PAGES(sizeof(CPU::x64::FXState) + 1));
memset(Thread->FPU, 0, FROM_PAGES(TO_PAGES(sizeof(CPU::x64::FXState))));
Thread->Security.TrustLevel = Parent->Security.TrustLevel;
Thread->Security.UniqueToken = SecurityManager.CreateToken();
// TODO: Is really a good idea to use the FPU in kernel mode?
Thread->FPU->mxcsr = 0b0001111110000000;
Thread->FPU->mxcsrmask = 0b1111111110111111;
Thread->FPU->fcw = 0b0000001100111111;
CPU::x64::fxrstor(Thread->FPU);
// uint16_t FCW = 0b1100111111;
// asmv("fldcw %0"
// :
// : "m"(FCW)
// : "memory");
// uint32_t MXCSR = 0b1111110000000;
// asmv("ldmxcsr %0"
// :
// : "m"(MXCSR)
// : "memory");
// CPU::x64::fxsave(Thread->FPU);
#if defined(a64)
memset(&Thread->Registers, 0, sizeof(CPU::x64::TrapFrame)); // Just in case
Thread->Registers.rip = (EntryPoint + Offset);
#elif defined(a32)
#elif defined(aa64)
#endif
switch (Parent->Security.TrustLevel)
{
case TaskTrustLevel::System:
warn("Trust level not supported.");
[[fallthrough]];
case TaskTrustLevel::Kernel:
{
Thread->Security.IsCritical = true;
Thread->Stack = new Memory::StackGuard(false, Parent->PageTable);
#if defined(a64)
SecurityManager.TrustToken(Thread->Security.UniqueToken, TTL::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 = ((uintptr_t)Thread->Stack->GetStackTop());
POKE(uintptr_t, Thread->Registers.rsp) = (uintptr_t)ThreadDoExit;
#elif defined(a32)
#elif defined(aa64)
#endif
break;
}
case TaskTrustLevel::User:
{
Thread->Stack = new Memory::StackGuard(true, Parent->PageTable);
#if defined(a64)
SecurityManager.TrustToken(Thread->Security.UniqueToken, TTL::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.IF = 1;
Thread->Registers.rflags.ID = 1;
Thread->Registers.rsp = ((uintptr_t)Thread->Stack->GetStackTop());
#pragma region
size_t ArgvSize = 0;
if (argv)
while (argv[ArgvSize] != nullptr)
ArgvSize++;
size_t EnvpSize = 0;
if (envp)
while (envp[EnvpSize] != nullptr)
EnvpSize++;
debug("ArgvSize: %d", ArgvSize);
debug("EnvpSize: %d", EnvpSize);
/* https://articles.manugarg.com/aboutelfauxiliaryvectors.html */
/* https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf#figure.3.9 */
// rsp is the top of the stack
char *Stack = (char *)Thread->Stack->GetStackPhysicalTop();
// Temporary stack pointer for strings
char *StackStrings = (char *)Stack;
char *StackStringsVirtual = (char *)Thread->Stack->GetStackTop();
// Store string pointers for later
uintptr_t *ArgvStrings = new uintptr_t[ArgvSize];
uintptr_t *EnvpStrings = new uintptr_t[EnvpSize];
for (size_t i = 0; i < ArgvSize; i++)
{
// Subtract the length of the string and the null terminator
StackStrings -= strlen(argv[i]) + 1;
StackStringsVirtual -= strlen(argv[i]) + 1;
// Store the pointer to the string
ArgvStrings[i] = (uintptr_t)StackStringsVirtual;
// Copy the string to the stack
strcpy(StackStrings, argv[i]);
}
for (size_t i = 0; i < EnvpSize; i++)
{
// Subtract the length of the string and the null terminator
StackStrings -= strlen(envp[i]) + 1;
StackStringsVirtual -= strlen(envp[i]) + 1;
// Store the pointer to the string
EnvpStrings[i] = (uintptr_t)StackStringsVirtual;
// Copy the string to the stack
strcpy(StackStrings, envp[i]);
}
// Align the stack to 16 bytes
StackStrings -= (uintptr_t)StackStrings & 0xF;
// Set "Stack" to the new stack pointer
Stack = (char *)StackStrings;
// If argv and envp sizes are odd then we need to align the stack
Stack -= (ArgvSize + EnvpSize) % 2;
// We need 8 bit pointers for the stack from here
uintptr_t *Stack64 = (uintptr_t *)Stack;
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// auxv_array is initialized with auxv elements. If the array is empty then we add a null terminator
std::vector<AuxiliaryVector> auxv_array = auxv;
if (auxv_array.size() == 0)
auxv_array.push_back({.archaux = {.a_type = AT_NULL, .a_un = {.a_val = 0}}});
// Store auxillary vector
foreach (AuxiliaryVector var in auxv_array)
{
// Subtract the size of the auxillary vector
Stack64 -= sizeof(Elf64_auxv_t) / sizeof(uintptr_t);
// Store the auxillary vector
POKE(Elf64_auxv_t, Stack64) = var.archaux;
// TODO: Store strings to the stack
}
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// Store EnvpStrings[] to the stack
Stack64 -= EnvpSize; // (1 Stack64 = 8 bits; Stack64 = 8 * EnvpSize)
for (size_t i = 0; i < EnvpSize; i++)
{
*(Stack64 + i) = (uintptr_t)EnvpStrings[i];
debug("EnvpStrings[%d]: %#lx", i, EnvpStrings[i]);
}
// Store the null terminator
Stack64--;
*Stack64 = AT_NULL;
// Store ArgvStrings[] to the stack
Stack64 -= ArgvSize; // (1 Stack64 = 8 bits; Stack64 = 8 * ArgvSize)
for (size_t i = 0; i < ArgvSize; i++)
{
*(Stack64 + i) = (uintptr_t)ArgvStrings[i];
debug("ArgvStrings[%d]: %#lx", i, ArgvStrings[i]);
}
// Store the argc
Stack64--;
*Stack64 = ArgvSize;
// Set "Stack" to the new stack pointer
Stack = (char *)Stack64;
/* We need the virtual address but because we are in the kernel we can't use the process page table.
So we modify the physical address and store how much we need to subtract to get the virtual address for RSP. */
uintptr_t SubtractStack = (uintptr_t)Thread->Stack->GetStackPhysicalTop() - (uintptr_t)Stack;
debug("SubtractStack: %#lx", SubtractStack);
// Set the stack pointer to the new stack
Thread->Registers.rsp = ((uintptr_t)Thread->Stack->GetStackTop() - SubtractStack);
delete[] ArgvStrings;
delete[] EnvpStrings;
#ifdef DEBUG
DumpData("Stack Data", (void *)((uintptr_t)Thread->Stack->GetStackPhysicalTop() - (uintptr_t)SubtractStack), SubtractStack);
#endif
Thread->Registers.rdi = (uintptr_t)ArgvSize; // argc
Thread->Registers.rsi = (uintptr_t)(Thread->Registers.rsp + 8); // argv
Thread->Registers.rcx = (uintptr_t)EnvpSize; // envc
Thread->Registers.rdx = (uintptr_t)(Thread->Registers.rsp + 8 + (8 * ArgvSize) + 8); // envp
#pragma endregion
/* 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(a32)
#elif defined(aa64)
#endif
#ifdef DEBUG_TASKING
DumpData(Thread->Name, Thread->Stack, STACK_SIZE);
#endif
break;
}
default:
{
error("Unknown elevation.");
delete Thread->Stack;
this->NextTID--;
delete Thread;
return nullptr;
}
}
Thread->Info = {};
Thread->Info.SpawnTime = TimeManager->GetCounter();
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.Affinity[i] = true;
}
Thread->Info.Priority = TaskPriority::Normal;
Thread->Info.Architecture = Architecture;
Thread->Info.Compatibility = Compatibility;
#ifdef DEBUG
#ifdef a64
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);
#elif defined(a32)
debug("Thread offset is %#lx (EntryPoint: %#lx) => RIP: %#lx", Thread->Offset, Thread->EntryPoint, Thread->Registers.eip);
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.esp);
else
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx", Thread->Stack->GetStackBottom(), Thread->Stack->GetStackTop(), Thread->Registers.esp);
#elif defined(aa64)
#endif
debug("Created thread \"%s\"(%d) in process \"%s\"(%d)",
Thread->Name, Thread->ID,
Thread->Parent->Name, Thread->Parent->ID);
#endif
Parent->Threads.push_back(Thread);
return Thread;
}
PCB *Task::CreateProcess(PCB *Parent,
const char *Name,
TaskTrustLevel TrustLevel, void *Image,
bool DoNotCreatePageTable)
{
SmartLock(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();
char ProcFSName[16];
sprintf(ProcFSName, "%ld", Process->ID);
Process->ProcessDirectory = vfs->Create(ProcFSName, VirtualFileSystem::NodeFlags::DIRECTORY, ProcFS);
Process->memDirectory = vfs->Create("mem", VirtualFileSystem::NodeFlags::DIRECTORY, Process->ProcessDirectory);
Process->IPC = new InterProcessCommunication::IPC((void *)Process);
switch (TrustLevel)
{
case TaskTrustLevel::System:
warn("Trust level not supported.");
[[fallthrough]];
case TaskTrustLevel::Kernel:
{
Process->Security.IsCritical = true;
SecurityManager.TrustToken(Process->Security.UniqueToken, TTL::TrustedByKernel);
#if defined(a64)
if (!DoNotCreatePageTable)
Process->PageTable = (Memory::PageTable4 *)CPU::x64::readcr3().raw;
#elif defined(a32)
#elif defined(aa64)
#endif
break;
}
case TaskTrustLevel::User:
{
SecurityManager.TrustToken(Process->Security.UniqueToken, TTL::Untrusted);
#if defined(a64)
if (!DoNotCreatePageTable)
{
Process->PageTable = (Memory::PageTable4 *)KernelAllocator.RequestPages(TO_PAGES(sizeof(Memory::PageTable4) + 1));
memcpy(Process->PageTable, (void *)KernelPageTable, PAGE_SIZE);
}
#elif defined(a32)
#elif defined(aa64)
#endif
break;
}
default:
{
error("Unknown elevation.");
this->NextPID--;
delete Process;
return nullptr;
}
}
Process->Info = {};
Process->Info.SpawnTime = TimeManager->GetCounter();
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.Affinity[i] = true;
}
Process->Info.Priority = TaskPriority::Normal;
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 (Image)
{
// TODO: Check if it's ELF
Process->ELFSymbolTable = new SymbolResolver::Symbols((uintptr_t)Image);
}
else
{
debug("No image provided for process \"%s\"(%d)", Process->Name, Process->ID);
}
if (Parent)
Parent->Children.push_back(Process);
ProcessList.push_back(Process);
return Process;
}
Task::Task(const IP EntryPoint) : Interrupts::Handler(16) /* IRQ16 */
{
SmartLock(TaskingLock);
#if defined(a64)
// Map the IRQ16 to the first CPU.
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1);
#elif defined(a32)
#elif defined(aa64)
#endif
KPrint("Starting Tasking With Instruction Pointer: %p (\e666666%s\eCCCCCC)", EntryPoint, KernelSymbolTable->GetSymbolFromAddress(EntryPoint));
TaskingLock.Unlock();
#if defined(a64)
TaskArchitecture Arch = TaskArchitecture::x64;
#elif defined(a32)
TaskArchitecture Arch = TaskArchitecture::x32;
#elif defined(aa64)
TaskArchitecture Arch = TaskArchitecture::ARM64;
#endif
PCB *kproc = CreateProcess(nullptr, "Kernel", TaskTrustLevel::Kernel);
TCB *kthrd = CreateThread(kproc, EntryPoint, nullptr, nullptr, std::vector<AuxiliaryVector>(), 0, Arch);
kthrd->Rename("Main Thread");
debug("Created Kernel Process: %s and Thread: %s", kproc->Name, kthrd->Name);
TaskingLock.Lock(__FUNCTION__);
bool MONITORSupported = false;
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
cpuid.Get();
MONITORSupported = cpuid.ECX.MONITOR;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
cpuid.Get();
MONITORSupported = cpuid.ECX.MONITOR;
}
if (MONITORSupported)
{
trace("CPU has MONITOR/MWAIT support.");
}
if (!CPU::Interrupts(CPU::Check))
{
error("Interrupts are not enabled.");
CPU::Interrupts(CPU::Enable);
}
TaskingLock.Unlock();
IdleProcess = CreateProcess(nullptr, (char *)"Idle", TaskTrustLevel::Kernel);
for (int i = 0; i < SMP::CPUCores; i++)
{
IdleThread = CreateThread(IdleProcess, reinterpret_cast<uintptr_t>(IdleProcessLoop));
char IdleName[16];
sprintf(IdleName, "Idle Thread %d", i);
IdleThread->Rename(IdleName);
IdleThread->SetPriority(Idle);
for (int j = 0; j < MAX_CPU; j++)
IdleThread->Info.Affinity[j] = false;
IdleThread->Info.Affinity[i] = true;
}
debug("Tasking Started");
#if defined(a64)
((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x86::IRQ16, 100);
/* FIXME: The kernel is not ready for multi-core tasking. */
// for (int i = 1; i < SMP::CPUCores; i++)
// {
// ((APIC::Timer *)Interrupts::apicTimer[i])->OneShot(CPU::x86::IRQ16, 100);
// APIC::InterruptCommandRegisterLow icr;
// icr.Vector = CPU::x86::IRQ16;
// icr.Level = APIC::APICLevel::Assert;
// ((APIC::APIC *)Interrupts::apic[0])->IPI(i, icr);
// }
#elif defined(a32)
#elif defined(aa64)
#endif
}
Task::~Task()
{
debug("Destructor called");
{
SmartLock(TaskingLock);
foreach (PCB *Process in ProcessList)
{
foreach (TCB *Thread in Process->Threads)
{
if (Thread == GetCurrentCPU()->CurrentThread.load() ||
Thread == CleanupThread)
continue;
this->KillThread(Thread, 0xFFFF);
}
if (Process == GetCurrentCPU()->CurrentProcess.load())
continue;
this->KillProcess(Process, 0xFFFF);
}
}
while (ProcessList.size() > 0)
{
trace("Waiting for %d processes to terminate", ProcessList.size());
int NotTerminated = 0;
foreach (PCB *Process in ProcessList)
{
debug("Process %s(%d) is still running (or waiting to be removed status %#lx)", Process->Name, Process->ID, Process->Status);
if (Process->Status == TaskStatus::Terminated)
continue;
NotTerminated++;
}
if (NotTerminated == 1)
break;
TaskingScheduler_OneShot(100);
}
trace("Tasking stopped");
}
}