mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-05-25 22:14:37 +00:00
897 lines
32 KiB
C++
897 lines
32 KiB
C++
/*
|
|
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 < ListProcess.size(); i++)
|
|
{
|
|
if (ListProcess[i] == Process)
|
|
{
|
|
trace("Process \"%s\"(%d) removed from the list", Process->Name, Process->ID);
|
|
// Free memory
|
|
delete ListProcess[i]->IPC, ListProcess[i]->IPC = nullptr;
|
|
delete ListProcess[i]->ELFSymbolTable, ListProcess[i]->ELFSymbolTable = nullptr;
|
|
SecurityManager.DestroyToken(ListProcess[i]->Security.UniqueToken);
|
|
if (ListProcess[i]->Security.TrustLevel == TaskTrustLevel::User)
|
|
KernelAllocator.FreePages((void *)ListProcess[i]->PageTable, TO_PAGES(sizeof(Memory::PageTable4) + 1));
|
|
|
|
// Remove the process from parent's children list
|
|
if (ListProcess[i]->Parent)
|
|
for (size_t j = 0; j < ListProcess[i]->Parent->Children.size(); j++)
|
|
{
|
|
if (ListProcess[i]->Parent->Children[j] == ListProcess[i])
|
|
{
|
|
ListProcess[i]->Parent->Children.remove(j);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Delete process directory
|
|
vfs->Delete(ListProcess[i]->ProcessDirectory, true);
|
|
|
|
// Free memory
|
|
delete ListProcess[i], ListProcess[i] = nullptr;
|
|
// Remove from the list
|
|
ListProcess.remove(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (TCB *thread in Process->Threads)
|
|
if (thread->Status == Terminated)
|
|
RemoveThread(thread);
|
|
}
|
|
}
|
|
|
|
SafeFunction NIF void Task::UpdateUserTime(TaskInfo *Info)
|
|
{
|
|
// TODO
|
|
Info->UserTime++;
|
|
}
|
|
|
|
SafeFunction NIF void Task::UpdateKernelTime(TaskInfo *Info)
|
|
{
|
|
// TODO
|
|
Info->KernelTime++;
|
|
}
|
|
|
|
SafeFunction NIF 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;
|
|
}
|
|
}
|
|
|
|
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 < ListProcess.size(); i++)
|
|
if (ListProcess[i]->ID == ID)
|
|
return ListProcess[i];
|
|
return nullptr;
|
|
}
|
|
|
|
TCB *Task::GetThreadByID(UTID ID)
|
|
{
|
|
for (size_t i = 0; i < ListProcess.size(); i++)
|
|
for (size_t j = 0; j < ListProcess[i]->Threads.size(); j++)
|
|
if (ListProcess[i]->Threads[j]->ID == ID)
|
|
return ListProcess[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);
|
|
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 ListProcess)
|
|
{
|
|
if (InvalidPCB(process))
|
|
continue;
|
|
|
|
RemoveProcess(process);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Task::RevertProcessCreation(PCB *Process)
|
|
{
|
|
for (size_t i = 0; i < ListProcess.size(); i++)
|
|
{
|
|
if (ListProcess[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;
|
|
ListProcess.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->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 = 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 = 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:
|
|
{
|
|
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 = 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 = 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);
|
|
ListProcess.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 ListProcess)
|
|
{
|
|
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 (ListProcess.size() > 0)
|
|
{
|
|
trace("Waiting for %d processes to terminate", ListProcess.size());
|
|
int NotTerminated = 0;
|
|
foreach (PCB *Process in ListProcess)
|
|
{
|
|
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");
|
|
}
|
|
}
|