mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-01 18:39:16 +00:00
Merge remote-tracking branch 'Kernel/master'
This commit is contained in:
332
Kernel/tasking/process.cpp
Normal file
332
Kernel/tasking/process.cpp
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
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 <signal.hpp>
|
||||
#include <convert.h>
|
||||
#include <lock.hpp>
|
||||
#include <printf.h>
|
||||
#include <smp.hpp>
|
||||
#include <io.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
#if defined(a64)
|
||||
#include "../arch/amd64/cpu/apic.hpp"
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/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
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
TCB *PCB::GetThread(TID ID)
|
||||
{
|
||||
auto it = std::find_if(this->Threads.begin(), this->Threads.end(),
|
||||
[ID](TCB *t)
|
||||
{ return t->ID == ID; });
|
||||
return it != this->Threads.end() ? *it : nullptr;
|
||||
}
|
||||
|
||||
int PCB::SendSignal(int sig)
|
||||
{
|
||||
return this->Signals.SendSignal((enum Signals)sig);
|
||||
}
|
||||
|
||||
void PCB::SetState(TaskState state)
|
||||
{
|
||||
this->State.store(state);
|
||||
if (this->Threads.size() == 1)
|
||||
this->Threads.front()->State.store(state);
|
||||
}
|
||||
|
||||
void PCB::SetExitCode(int code)
|
||||
{
|
||||
this->ExitCode.store(code);
|
||||
if (this->Threads.size() == 1)
|
||||
this->Threads.front()->ExitCode.store(code);
|
||||
}
|
||||
|
||||
void PCB::Rename(const char *name)
|
||||
{
|
||||
assert(name != nullptr);
|
||||
assert(strlen(name) > 0);
|
||||
|
||||
trace("Renaming thread %s to %s",
|
||||
this->Name, name);
|
||||
|
||||
if (this->Name)
|
||||
{
|
||||
this->AllocatedMemory -= strlen(this->Name) + 1;
|
||||
delete[] this->Name;
|
||||
}
|
||||
|
||||
this->Name = new char[strlen(name) + 1];
|
||||
this->AllocatedMemory += strlen(name) + 1;
|
||||
strcpy((char *)this->Name, name);
|
||||
}
|
||||
|
||||
void PCB::SetWorkingDirectory(FileNode *node)
|
||||
{
|
||||
trace("Setting working directory of process %s to %#lx (%s)",
|
||||
this->Name, node, node->Name.c_str());
|
||||
CWD = node;
|
||||
FileNode *cwd = fs->GetByPath("cwd", ProcDirectory);
|
||||
if (cwd)
|
||||
fs->Remove(cwd);
|
||||
cwd = fs->CreateLink("cwd", ProcDirectory, node);
|
||||
if (cwd == nullptr)
|
||||
error("Failed to create cwd link");
|
||||
}
|
||||
|
||||
void PCB::SetExe(const char *path)
|
||||
{
|
||||
trace("Setting exe %s to %s",
|
||||
this->Name, path);
|
||||
Executable = fs->GetByPath(path, ProcDirectory);
|
||||
FileNode *exe = fs->GetByPath("exe", ProcDirectory);
|
||||
if (exe)
|
||||
fs->Remove(exe);
|
||||
exe = fs->CreateLink("exe", ProcDirectory, path);
|
||||
if (exe == nullptr)
|
||||
error("Failed to create exe link");
|
||||
}
|
||||
|
||||
size_t PCB::GetSize()
|
||||
{
|
||||
size_t ret = this->AllocatedMemory;
|
||||
ret += this->vma->GetAllocatedMemorySize();
|
||||
|
||||
for (size_t i = 0; i < this->Threads.size(); i++)
|
||||
ret += sizeof(TCB);
|
||||
|
||||
for (size_t i = 0; i < this->Children.size(); i++)
|
||||
ret += sizeof(PCB);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PCB::PCB(Task *ctx, PCB *Parent, const char *Name,
|
||||
TaskExecutionMode ExecutionMode,
|
||||
bool UseKernelPageTable,
|
||||
uint16_t UserID, uint16_t GroupID)
|
||||
: Signals(this)
|
||||
{
|
||||
debug("+ %#lx", this);
|
||||
|
||||
assert(ctx != nullptr);
|
||||
assert(Name != nullptr);
|
||||
assert(strlen(Name) > 0);
|
||||
assert(ExecutionMode >= _ExecuteModeMin);
|
||||
assert(ExecutionMode <= _ExecuteModeMax);
|
||||
|
||||
FileNode *procDir = fs->GetByPath("/proc", nullptr);
|
||||
assert(procDir != nullptr);
|
||||
|
||||
/* d r-x r-x r-x */
|
||||
mode_t mode = S_IROTH | S_IXOTH |
|
||||
S_IRGRP | S_IXGRP |
|
||||
S_IRUSR | S_IXUSR |
|
||||
S_IFDIR;
|
||||
|
||||
ProcDirectory = fs->Create(procDir, std::to_string(ctx->NextPID).c_str(), mode);
|
||||
assert(ProcDirectory != nullptr);
|
||||
|
||||
this->ctx = ctx;
|
||||
this->ID = ctx->NextPID++;
|
||||
|
||||
if (this->Name) /* Prevent memory leak */
|
||||
delete[] this->Name;
|
||||
|
||||
this->Name = new char[strlen(Name) + 1];
|
||||
strcpy((char *)this->Name, Name);
|
||||
this->ExitCode = KILL_CRASH;
|
||||
|
||||
/* Check parent */
|
||||
if (Parent == nullptr)
|
||||
this->Parent = ctx->GetCurrentProcess();
|
||||
else
|
||||
this->Parent = Parent;
|
||||
|
||||
/* Set uid & gid */
|
||||
if (this->Parent &&
|
||||
UserID == UINT16_MAX &&
|
||||
GroupID == UINT16_MAX)
|
||||
{
|
||||
UserID = this->Parent->Security.Real.UserID;
|
||||
GroupID = this->Parent->Security.Real.GroupID;
|
||||
debug("Inherited uid & gid from parent process %s(%d) with uid %d and gid %d",
|
||||
this->Parent->Name, this->Parent->ID, UserID, GroupID);
|
||||
}
|
||||
|
||||
this->Security.Real.UserID = UserID;
|
||||
this->Security.Real.GroupID = GroupID;
|
||||
this->Security.Effective.UserID = UserID;
|
||||
this->Security.Effective.GroupID = GroupID;
|
||||
this->Security.ExecutionMode = ExecutionMode;
|
||||
|
||||
switch (ExecutionMode)
|
||||
{
|
||||
case TaskExecutionMode::System:
|
||||
fixme("Mode not supported.");
|
||||
[[fallthrough]];
|
||||
case TaskExecutionMode::Kernel:
|
||||
{
|
||||
this->Security.IsCritical = true;
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode::User:
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
this->FileDescriptors = new FileDescriptorTable(this);
|
||||
this->tty = nullptr;
|
||||
|
||||
/* If create page table */
|
||||
if (UseKernelPageTable == false)
|
||||
{
|
||||
OwnPageTable = true;
|
||||
this->PageTable = KernelPageTable->Fork();
|
||||
debug("Process %s(%d) has page table at %#lx",
|
||||
this->Name, this->ID, this->PageTable);
|
||||
}
|
||||
else
|
||||
this->PageTable = KernelPageTable;
|
||||
|
||||
this->vma = new Memory::VirtualMemoryArea(this->PageTable);
|
||||
this->ProgramBreak = new Memory::ProgramBreak(this->PageTable, this->vma);
|
||||
|
||||
debug("Process page table: %#lx", this->PageTable);
|
||||
debug("Created %s process \"%s\"(%d). Parent \"%s\"(%d)",
|
||||
ExecutionMode == TaskExecutionMode::User ? "user" : "kernel",
|
||||
this->Name, this->ID,
|
||||
Parent ? this->Parent->Name : "None",
|
||||
Parent ? this->Parent->ID : 0);
|
||||
|
||||
this->AllocatedMemory += strlen(Name) + 1;
|
||||
this->AllocatedMemory += sizeof(PCB);
|
||||
this->AllocatedMemory += sizeof(FileDescriptorTable);
|
||||
this->AllocatedMemory += FROM_PAGES(TO_PAGES(sizeof(Memory::PageTable) + 1));
|
||||
this->AllocatedMemory += sizeof(Memory::VirtualMemoryArea);
|
||||
this->AllocatedMemory += sizeof(Memory::ProgramBreak);
|
||||
this->AllocatedMemory += sizeof(SymbolResolver::Symbols);
|
||||
|
||||
this->Info.SpawnTime = TimeManager->GetCounter();
|
||||
|
||||
if (Parent)
|
||||
Parent->Children.push_back(this);
|
||||
ctx->PushProcess(this);
|
||||
}
|
||||
|
||||
PCB::~PCB()
|
||||
{
|
||||
debug("- %#lx", this);
|
||||
debug("Destroying process \"%s\"(%d)",
|
||||
this->Name, this->ID);
|
||||
|
||||
debug("Removing from process list");
|
||||
/* Remove us from the process list so we
|
||||
don't get scheduled anymore */
|
||||
ctx->PopProcess(this);
|
||||
|
||||
debug("Freeing allocated memory");
|
||||
delete this->ProgramBreak;
|
||||
delete this->vma;
|
||||
|
||||
debug("Closing file descriptors");
|
||||
delete this->FileDescriptors;
|
||||
|
||||
debug("Deleting tty");
|
||||
// if (this->tty)
|
||||
// delete ((TTY::TeletypeDriver *)this->tty);
|
||||
fixme("remove workarounds for stdio and tty");
|
||||
|
||||
/* FIXME: DON'T DELETE THE TTY
|
||||
spawn.cpp is using this as workaround
|
||||
tty == KernelConsole::CurrentTerminal.load();
|
||||
*/
|
||||
|
||||
/* If we own the pointer to the
|
||||
PageTable, we need to free it */
|
||||
if (this->PageTable && OwnPageTable)
|
||||
{
|
||||
debug("Freeing page table");
|
||||
size_t PTPgs = TO_PAGES(sizeof(Memory::PageTable) + 1);
|
||||
KernelAllocator.FreePages(this->PageTable, PTPgs);
|
||||
}
|
||||
|
||||
/* Exit all children processes */
|
||||
foreach (auto pcb in this->Children)
|
||||
{
|
||||
if (pcb == nullptr)
|
||||
{
|
||||
warn("Process is null? Kernel bug");
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Destroying child process \"%s\"(%d)",
|
||||
pcb->Name, pcb->ID);
|
||||
delete pcb;
|
||||
}
|
||||
|
||||
/* Exit all threads */
|
||||
foreach (auto tcb in this->Threads)
|
||||
{
|
||||
if (tcb == nullptr)
|
||||
{
|
||||
warn("Thread is null? Kernel bug");
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Destroying thread \"%s\"(%d)",
|
||||
tcb->Name, tcb->ID);
|
||||
delete tcb;
|
||||
}
|
||||
|
||||
/* Free Name */
|
||||
delete[] this->Name;
|
||||
|
||||
debug("Removing from parent process");
|
||||
if (likely(this->Parent))
|
||||
{
|
||||
this->Parent->Children.erase(std::find(this->Parent->Children.begin(),
|
||||
this->Parent->Children.end(), this));
|
||||
}
|
||||
|
||||
debug("Process \"%s\"(%d) destroyed",
|
||||
this->Name, this->ID);
|
||||
}
|
||||
}
|
792
Kernel/tasking/scheduler/custom.cpp
Normal file
792
Kernel/tasking/scheduler/custom.cpp
Normal file
@ -0,0 +1,792 @@
|
||||
/*
|
||||
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 <scheduler.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 "../arch/amd64/cpu/apic.hpp"
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/i386/cpu/apic.hpp"
|
||||
#include "../arch/i386/cpu/gdt.hpp"
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
// #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
|
||||
|
||||
__naked __used nsa void __custom_sched_idle_loop()
|
||||
{
|
||||
#if defined(a86)
|
||||
asmv("IdleLoop:");
|
||||
asmv("hlt");
|
||||
asmv("jmp IdleLoop");
|
||||
#elif defined(aa64)
|
||||
asmv("IdleLoop:");
|
||||
asmv("wfe");
|
||||
asmv("b IdleLoop");
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Tasking::Scheduler
|
||||
{
|
||||
bool Custom::RemoveThread(TCB *Thread)
|
||||
{
|
||||
debug("Thread \"%s\"(%d) removed from process \"%s\"(%d)",
|
||||
Thread->Name, Thread->ID, Thread->Parent->Name,
|
||||
Thread->Parent->ID);
|
||||
|
||||
delete Thread;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Custom::RemoveProcess(PCB *Process)
|
||||
{
|
||||
if (Process->State == Terminated)
|
||||
{
|
||||
delete Process;
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (TCB *Thread in Process->Threads)
|
||||
{
|
||||
if (Thread->State == Terminated)
|
||||
RemoveThread(Thread);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PCB *Custom::GetProcessByID(TID ID)
|
||||
{
|
||||
foreach (auto p in ProcessList)
|
||||
{
|
||||
if (p->ID == ID)
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TCB *Custom::GetThreadByID(TID ID, PCB *Parent)
|
||||
{
|
||||
if (unlikely(Parent == nullptr))
|
||||
return nullptr;
|
||||
|
||||
foreach (auto t in Parent->Threads)
|
||||
{
|
||||
if (t->ID == ID)
|
||||
return t;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Custom::StartIdleProcess()
|
||||
{
|
||||
IdleProcess = ctx->CreateProcess(nullptr, (char *)"Idle",
|
||||
TaskExecutionMode::Kernel, true);
|
||||
for (int i = 0; i < SMP::CPUCores; i++)
|
||||
{
|
||||
TCB *thd = ctx->CreateThread(IdleProcess, IP(__custom_sched_idle_loop));
|
||||
char IdleName[16];
|
||||
sprintf(IdleName, "Idle Thread %d", i);
|
||||
thd->Rename(IdleName);
|
||||
thd->SetPriority(Idle);
|
||||
for (int j = 0; j < MAX_CPU; j++)
|
||||
thd->Info.Affinity[j] = false;
|
||||
thd->Info.Affinity[i] = true;
|
||||
|
||||
if (unlikely(i == 0))
|
||||
IdleThread = thd;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PCB *> &Custom::GetProcessList()
|
||||
{
|
||||
return ProcessList;
|
||||
}
|
||||
|
||||
void Custom::StartScheduler()
|
||||
{
|
||||
#if defined(a86)
|
||||
if (Interrupts::apicTimer[0])
|
||||
{
|
||||
((APIC::Timer *)Interrupts::apicTimer[0])->OneShot(CPU::x86::IRQ16, 100);
|
||||
|
||||
/* FIXME: The kernel is not ready for multi-core tasking. */
|
||||
return;
|
||||
|
||||
APIC::InterruptCommandRegister icr{};
|
||||
bool x2APIC = ((APIC::APIC *)Interrupts::apic[0])->x2APIC;
|
||||
|
||||
if (likely(x2APIC))
|
||||
{
|
||||
icr.x2.VEC = s_cst(uint8_t, CPU::x86::IRQ16);
|
||||
icr.x2.MT = APIC::Fixed;
|
||||
icr.x2.L = APIC::Assert;
|
||||
icr.x2.DES = 0xFFFFFFFF; /* Broadcast IPI to all local APICs. */
|
||||
((APIC::APIC *)Interrupts::apic[0])->ICR(icr);
|
||||
}
|
||||
else
|
||||
{
|
||||
icr.VEC = s_cst(uint8_t, CPU::x86::IRQ16);
|
||||
icr.MT = APIC::Fixed;
|
||||
icr.L = APIC::Assert;
|
||||
|
||||
for (int i = 0; i < SMP::CPUCores; i++)
|
||||
{
|
||||
icr.DES = uint8_t(i);
|
||||
((APIC::APIC *)Interrupts::apic[i])->ICR(icr);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Custom::Yield()
|
||||
{
|
||||
/* This will trigger the IRQ16
|
||||
instantly so we won't execute
|
||||
the next instruction */
|
||||
#if defined(a86)
|
||||
asmv("int $0x30");
|
||||
#elif defined(aa64)
|
||||
asmv("svc #0x30");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Custom::PushProcess(PCB *pcb)
|
||||
{
|
||||
this->ProcessList.push_back(pcb);
|
||||
}
|
||||
|
||||
void Custom::PopProcess(PCB *pcb)
|
||||
{
|
||||
auto it = std::find(this->ProcessList.begin(),
|
||||
this->ProcessList.end(), pcb);
|
||||
|
||||
if (it == this->ProcessList.end())
|
||||
{
|
||||
debug("Process %d not found in the list", pcb->ID);
|
||||
return;
|
||||
}
|
||||
|
||||
this->ProcessList.erase(it);
|
||||
}
|
||||
|
||||
std::pair<PCB *, TCB *> Custom::GetIdle()
|
||||
{
|
||||
return std::make_pair(IdleProcess, IdleThread);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- */
|
||||
|
||||
nsa void Custom::OneShot(int TimeSlice)
|
||||
{
|
||||
if (TimeSlice == 0)
|
||||
TimeSlice = Tasking::TaskPriority::Normal;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (DebuggerIsAttached)
|
||||
TimeSlice += 10;
|
||||
#endif
|
||||
|
||||
#if defined(a86)
|
||||
((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice);
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
}
|
||||
|
||||
nsa void Custom::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core)
|
||||
{
|
||||
UNUSED(Core);
|
||||
uint64_t CurrentTime = TimeManager->GetCounter();
|
||||
uint64_t TimePassed = Info->LastUpdateTime - CurrentTime;
|
||||
Info->LastUpdateTime = CurrentTime;
|
||||
|
||||
if (Mode == TaskExecutionMode::User)
|
||||
Info->UserTime += TimePassed;
|
||||
else
|
||||
Info->KernelTime += TimePassed;
|
||||
}
|
||||
|
||||
nsa NIF bool Custom::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)
|
||||
{
|
||||
switch (process->State.load())
|
||||
{
|
||||
case TaskState::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->State);
|
||||
|
||||
/* 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 (thread->State.load() != TaskState::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;
|
||||
}
|
||||
|
||||
nsa NIF bool Custom::GetNextAvailableThread(void *CPUDataPointer)
|
||||
{
|
||||
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
||||
|
||||
size_t ThreadsSize = CurrentCPU->CurrentProcess->Threads.size();
|
||||
|
||||
for (size_t i = 0; i < ThreadsSize; i++)
|
||||
{
|
||||
if (CurrentCPU->CurrentProcess->Threads[i] == CurrentCPU->CurrentThread.load())
|
||||
{
|
||||
size_t TempIndex = i;
|
||||
RetryAnotherThread:
|
||||
if (TempIndex + 1 >= ThreadsSize)
|
||||
break;
|
||||
|
||||
TCB *nextThread = CurrentCPU->CurrentProcess->Threads[TempIndex + 1];
|
||||
|
||||
gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)",
|
||||
CurrentCPU->CurrentProcess->Threads[i]->Name,
|
||||
CurrentCPU->CurrentProcess->Threads[i]->ID,
|
||||
nextThread->Name, nextThread->ID);
|
||||
|
||||
if (nextThread->State.load() != TaskState::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",
|
||||
nextThread->ID, nextThread->Parent->Name,
|
||||
ThreadsSize, ProcessList.size());
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
{
|
||||
gnat_schedbg("Thread %d is not the current one",
|
||||
CurrentCPU->CurrentProcess->Threads[i]->ID);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsa NIF bool Custom::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 (process->State.load() != TaskState::Ready)
|
||||
{
|
||||
gnap_schedbg("Process %d is not ready", process->ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (thread->State.load() != TaskState::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;
|
||||
}
|
||||
|
||||
nsa NIF bool Custom::SchedulerSearchProcessThread(void *CPUDataPointer)
|
||||
{
|
||||
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
||||
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (process->State.load() != TaskState::Ready)
|
||||
{
|
||||
sspt_schedbg("Process %d is not ready", process->ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (thread->State.load() != TaskState::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;
|
||||
}
|
||||
|
||||
nsa NIF void Custom::UpdateProcessState()
|
||||
{
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (process->State.load() == TaskState::Terminated)
|
||||
continue;
|
||||
|
||||
if (process->Threads.size() == 1)
|
||||
{
|
||||
process->State.exchange(process->Threads.front()->State.load());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool AllThreadsSleeping = true;
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (thread->State.load() == TaskState::Terminated)
|
||||
continue;
|
||||
|
||||
if (thread->State.load() != TaskState::Sleeping)
|
||||
{
|
||||
AllThreadsSleeping = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (AllThreadsSleeping)
|
||||
process->State.store(TaskState::Sleeping);
|
||||
else if (process->State.load() == TaskState::Sleeping)
|
||||
process->State.store(TaskState::Ready);
|
||||
}
|
||||
}
|
||||
|
||||
nsa NIF void Custom::WakeUpThreads()
|
||||
{
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
Tasking::TaskState pState = process->State.load();
|
||||
if (pState != TaskState::Ready &&
|
||||
pState != TaskState::Sleeping &&
|
||||
pState != TaskState::Blocked)
|
||||
continue;
|
||||
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (likely(thread->State.load() != TaskState::Sleeping))
|
||||
continue;
|
||||
|
||||
/* Check if the thread is ready to wake up. */
|
||||
if (unlikely(thread->Info.SleepUntil < TimeManager->GetCounter()))
|
||||
{
|
||||
if (pState == TaskState::Sleeping)
|
||||
process->State.store(TaskState::Ready);
|
||||
thread->State.store(TaskState::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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsa NIF void Custom::CleanupTerminated()
|
||||
{
|
||||
foreach (auto pcb in ProcessList)
|
||||
{
|
||||
if (pcb->State.load() == TaskState::Terminated)
|
||||
{
|
||||
debug("Found terminated process %s(%d)", pcb->Name, pcb->ID);
|
||||
delete pcb;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (TCB *tcb in pcb->Threads)
|
||||
{
|
||||
if (tcb->State == Terminated)
|
||||
delete tcb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsa NIF void Custom::Schedule(CPU::SchedulerFrame *Frame)
|
||||
{
|
||||
if (unlikely(StopScheduler))
|
||||
{
|
||||
warn("Scheduler stopped.");
|
||||
return;
|
||||
}
|
||||
bool ProcessNotChanged = false;
|
||||
uint64_t SchedTmpTicks = TimeManager->GetCounter();
|
||||
this->LastTaskTicks.store(size_t(SchedTmpTicks - this->SchedulerTicks.load()));
|
||||
CPUData *CurrentCPU = GetCurrentCPU();
|
||||
this->LastCore.store(CurrentCPU->ID);
|
||||
schedbg("Scheduler called on CPU %d.", CurrentCPU->ID);
|
||||
|
||||
if (unlikely(!CurrentCPU->CurrentProcess.load() ||
|
||||
!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);
|
||||
#ifdef a64
|
||||
CurrentCPU->CurrentThread->ShadowGSBase = CPU::x64::rdmsr(CPU::x64::MSR_SHADOW_GS_BASE);
|
||||
CurrentCPU->CurrentThread->GSBase = CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE);
|
||||
CurrentCPU->CurrentThread->FSBase = CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE);
|
||||
#else
|
||||
CurrentCPU->CurrentThread->ShadowGSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_SHADOW_GS_BASE));
|
||||
CurrentCPU->CurrentThread->GSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_GS_BASE));
|
||||
CurrentCPU->CurrentThread->FSBase = uintptr_t(CPU::x32::rdmsr(CPU::x32::MSR_FS_BASE));
|
||||
#endif
|
||||
|
||||
if (CurrentCPU->CurrentProcess->State.load() == TaskState::Running)
|
||||
CurrentCPU->CurrentProcess->State.store(TaskState::Ready);
|
||||
if (CurrentCPU->CurrentThread->State.load() == TaskState::Running)
|
||||
CurrentCPU->CurrentThread->State.store(TaskState::Ready);
|
||||
|
||||
this->CleanupTerminated();
|
||||
schedbg("Passed CleanupTerminated");
|
||||
|
||||
this->UpdateProcessState();
|
||||
schedbg("Passed UpdateProcessState");
|
||||
|
||||
this->WakeUpThreads();
|
||||
schedbg("Passed WakeUpThreads");
|
||||
|
||||
if (this->SchedulerUpdateTrapFrame)
|
||||
{
|
||||
debug("Updating trap frame");
|
||||
this->SchedulerUpdateTrapFrame = false;
|
||||
CurrentCPU->CurrentProcess->State.store(TaskState::Running);
|
||||
CurrentCPU->CurrentThread->State.store(TaskState::Running);
|
||||
*Frame = CurrentCPU->CurrentThread->Registers;
|
||||
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->GetNextAvailableThread(CurrentCPU))
|
||||
{
|
||||
ProcessNotChanged = true;
|
||||
goto Success;
|
||||
}
|
||||
schedbg("Passed GetNextAvailableThread");
|
||||
|
||||
if (this->GetNextAvailableProcess(CurrentCPU))
|
||||
{
|
||||
goto Success;
|
||||
}
|
||||
schedbg("Passed GetNextAvailableProcess");
|
||||
|
||||
if (SchedulerSearchProcessThread(CurrentCPU))
|
||||
{
|
||||
schedbg("Passed SchedulerSearchProcessThread");
|
||||
goto Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
schedbg("SchedulerSearchProcessThread failed. Going idle.");
|
||||
goto Idle;
|
||||
}
|
||||
}
|
||||
|
||||
assert(!"Unwanted code execution");
|
||||
|
||||
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.ExecutionMode,
|
||||
CurrentCPU->ID);
|
||||
|
||||
UpdateUsage(&CurrentCPU->CurrentThread->Info,
|
||||
CurrentCPU->CurrentThread->Security.ExecutionMode,
|
||||
CurrentCPU->ID);
|
||||
|
||||
CurrentCPU->CurrentProcess->State.store(TaskState::Running);
|
||||
CurrentCPU->CurrentThread->State.store(TaskState::Running);
|
||||
|
||||
if (CurrentCPU->CurrentThread->Registers.cs != GDT_KERNEL_CODE)
|
||||
CurrentCPU->CurrentThread->Registers.ppt = (uint64_t)(void *)CurrentCPU->CurrentProcess->PageTable;
|
||||
else
|
||||
CurrentCPU->CurrentThread->Registers.ppt = (uint64_t)(void *)KernelPageTable;
|
||||
|
||||
// if (!SchedulerUpdateTrapFrame) {} // TODO
|
||||
|
||||
*Frame = CurrentCPU->CurrentThread->Registers;
|
||||
|
||||
#ifdef a64
|
||||
GlobalDescriptorTable::SetKernelStack((void *)((uintptr_t)CurrentCPU->CurrentThread->Stack->GetStackTop()));
|
||||
CPU::x64::fxrstor(&CurrentCPU->CurrentThread->FPU);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->ShadowGSBase);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase);
|
||||
#else
|
||||
GlobalDescriptorTable::SetKernelStack((void *)((uintptr_t)CurrentCPU->CurrentThread->Stack->GetStackTop()));
|
||||
CPU::x32::fxrstor(&CurrentCPU->CurrentThread->FPU);
|
||||
CPU::x32::wrmsr(CPU::x32::MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->ShadowGSBase);
|
||||
CPU::x32::wrmsr(CPU::x32::MSR_GS_BASE, CurrentCPU->CurrentThread->GSBase);
|
||||
CPU::x32::wrmsr(CPU::x32::MSR_FS_BASE, CurrentCPU->CurrentThread->FSBase);
|
||||
#endif
|
||||
|
||||
CurrentCPU->CurrentProcess->Signals.HandleSignal(Frame, CurrentCPU->CurrentThread.load());
|
||||
|
||||
if (!ProcessNotChanged)
|
||||
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
|
||||
(&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter();
|
||||
this->OneShot(CurrentCPU->CurrentThread->Info.Priority);
|
||||
|
||||
if (CurrentCPU->CurrentThread->Security.IsDebugEnabled &&
|
||||
CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled)
|
||||
{
|
||||
#ifdef a64
|
||||
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);
|
||||
#else
|
||||
trace("%s[%ld]: EIP=%#lx EBP=%#lx ESP=%#lx",
|
||||
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID,
|
||||
CurrentCPU->CurrentThread->Registers.eip,
|
||||
CurrentCPU->CurrentThread->Registers.ebp,
|
||||
CurrentCPU->CurrentThread->Registers.esp);
|
||||
#endif
|
||||
}
|
||||
|
||||
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
|
||||
}
|
||||
|
||||
nsa NIF void Custom::OnInterruptReceived(CPU::SchedulerFrame *Frame)
|
||||
{
|
||||
SmartCriticalSection(SchedulerLock);
|
||||
this->Schedule(Frame);
|
||||
}
|
||||
|
||||
Custom::Custom(Task *ctx) : Base(ctx), Interrupts::Handler(16) /* IRQ16 */
|
||||
{
|
||||
#if defined(a86)
|
||||
// Map the IRQ16 to the first CPU.
|
||||
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, CPU::x86::IRQ16 - CPU::x86::IRQ0, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
Custom::~Custom()
|
||||
{
|
||||
foreach (PCB *Process in ProcessList)
|
||||
{
|
||||
foreach (TCB *Thread in Process->Threads)
|
||||
{
|
||||
if (Thread == GetCurrentCPU()->CurrentThread.load())
|
||||
continue;
|
||||
ctx->KillThread(Thread, KILL_SCHEDULER_DESTRUCTION);
|
||||
}
|
||||
|
||||
if (Process == GetCurrentCPU()->CurrentProcess.load())
|
||||
continue;
|
||||
ctx->KillProcess(Process, KILL_SCHEDULER_DESTRUCTION);
|
||||
}
|
||||
|
||||
debug("Waiting for processes to terminate");
|
||||
uint64_t timeout = TimeManager->CalculateTarget(20, Time::Units::Seconds);
|
||||
while (this->GetProcessList().size() > 0)
|
||||
{
|
||||
trace("Waiting for %d processes to terminate", this->GetProcessList().size());
|
||||
int NotTerminated = 0;
|
||||
foreach (PCB *Process in this->GetProcessList())
|
||||
{
|
||||
trace("Process %s(%d) is still running (or waiting to be removed state %#lx)",
|
||||
Process->Name, Process->ID, Process->State);
|
||||
|
||||
if (Process->State == TaskState::Terminated)
|
||||
{
|
||||
debug("Process %s(%d) terminated", Process->Name, Process->ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
NotTerminated++;
|
||||
}
|
||||
if (NotTerminated == 1)
|
||||
break;
|
||||
|
||||
ctx->Sleep(1000);
|
||||
debug("Current working process is %s(%d)",
|
||||
ctx->GetCurrentProcess()->Name,
|
||||
ctx->GetCurrentProcess()->ID);
|
||||
|
||||
if (TimeManager->GetCounter() > timeout)
|
||||
{
|
||||
error("Timeout waiting for processes to terminate");
|
||||
break;
|
||||
}
|
||||
|
||||
this->OneShot(100);
|
||||
}
|
||||
}
|
||||
}
|
771
Kernel/tasking/signal.cpp
Normal file
771
Kernel/tasking/signal.cpp
Normal file
@ -0,0 +1,771 @@
|
||||
/*
|
||||
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 <signal.hpp>
|
||||
#include <dumper.hpp>
|
||||
#include <task.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(a64)
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/i386/cpu/gdt.hpp"
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
const char *sigStr[] = {
|
||||
/* 0 */ "INVALID",
|
||||
/* 1 */ "SIGABRT",
|
||||
/* 2 */ "SIGALRM",
|
||||
/* 3 */ "SIGBUS",
|
||||
/* 4 */ "SIGCHLD",
|
||||
/* 5 */ "SIGCONT",
|
||||
/* 6 */ "SIGFPE",
|
||||
/* 7 */ "SIGHUP",
|
||||
/* 8 */ "SIGILL",
|
||||
/* 9 */ "SIGINT",
|
||||
/* 10 */ "SIGKILL",
|
||||
/* 11 */ "SIGPIPE",
|
||||
/* 12 */ "SIGQUIT",
|
||||
/* 13 */ "SIGSEGV",
|
||||
/* 14 */ "SIGSTOP",
|
||||
/* 15 */ "SIGTERM",
|
||||
/* 16 */ "SIGTSTP",
|
||||
/* 17 */ "SIGTTIN",
|
||||
/* 18 */ "SIGTTOU",
|
||||
/* 19 */ "SIGUSR1",
|
||||
/* 20 */ "SIGUSR2",
|
||||
/* 21 */ "SIGPOLL",
|
||||
/* 22 */ "SIGPROF",
|
||||
/* 23 */ "SIGSYS",
|
||||
/* 24 */ "SIGTRAP",
|
||||
/* 25 */ "SIGURG",
|
||||
/* 26 */ "SIGVTALRM",
|
||||
/* 27 */ "SIGXCPU",
|
||||
/* 28 */ "SIGXFSZ",
|
||||
/* 29 */ "SIGCOMP1",
|
||||
/* 30 */ "SIGCOMP2",
|
||||
/* 31 */ "SIGCOMP3",
|
||||
/* 32 */ "SIGRTMIN",
|
||||
/* 33 */ "SIGRT_1",
|
||||
/* 34 */ "SIGRT_2",
|
||||
/* 35 */ "SIGRT_3",
|
||||
/* 36 */ "SIGRT_4",
|
||||
/* 37 */ "SIGRT_5",
|
||||
/* 38 */ "SIGRT_6",
|
||||
/* 39 */ "SIGRT_7",
|
||||
/* 40 */ "SIGRT_8",
|
||||
/* 41 */ "SIGRT_9",
|
||||
/* 42 */ "SIGRT_10",
|
||||
/* 43 */ "SIGRT_11",
|
||||
/* 44 */ "SIGRT_12",
|
||||
/* 45 */ "SIGRT_13",
|
||||
/* 46 */ "SIGRT_14",
|
||||
/* 47 */ "SIGRT_15",
|
||||
/* 48 */ "SIGRT_16",
|
||||
/* 49 */ "SIGRT_17",
|
||||
/* 50 */ "SIGRT_18",
|
||||
/* 51 */ "SIGRT_19",
|
||||
/* 52 */ "SIGRT_20",
|
||||
/* 53 */ "SIGRT_21",
|
||||
/* 54 */ "SIGRT_22",
|
||||
/* 55 */ "SIGRT_23",
|
||||
/* 56 */ "SIGRT_24",
|
||||
/* 57 */ "SIGRT_25",
|
||||
/* 58 */ "SIGRT_26",
|
||||
/* 59 */ "SIGRT_27",
|
||||
/* 60 */ "SIGRT_28",
|
||||
/* 61 */ "SIGRT_29",
|
||||
/* 62 */ "SIGRT_30",
|
||||
/* 63 */ "SIGRT_31",
|
||||
/* 64 */ "SIGRTMAX",
|
||||
};
|
||||
|
||||
const char *dispStr[] = {
|
||||
"SIG_TERM",
|
||||
"SIG_IGN ",
|
||||
"SIG_CORE",
|
||||
"SIG_STOP",
|
||||
"SIG_CONT",
|
||||
};
|
||||
#endif
|
||||
|
||||
extern "C" uintptr_t _sig_native_trampoline_start, _sig_native_trampoline_end;
|
||||
extern "C" uintptr_t _sig_linux_trampoline_start, _sig_linux_trampoline_end;
|
||||
|
||||
static const struct
|
||||
{
|
||||
Signals Signal;
|
||||
SignalDispositions Disposition;
|
||||
} SignalDisposition[] = {
|
||||
{SIGHUP, SIG_TERM},
|
||||
{SIGINT, SIG_TERM},
|
||||
{SIGQUIT, SIG_TERM},
|
||||
{SIGILL, SIG_CORE},
|
||||
{SIGTRAP, SIG_CORE},
|
||||
{SIGABRT, SIG_CORE},
|
||||
{SIGBUS, SIG_CORE},
|
||||
{SIGFPE, SIG_CORE},
|
||||
{SIGKILL, SIG_TERM},
|
||||
{SIGUSR1, SIG_TERM},
|
||||
{SIGSEGV, SIG_CORE},
|
||||
{SIGUSR2, SIG_TERM},
|
||||
{SIGPIPE, SIG_TERM},
|
||||
{SIGALRM, SIG_TERM},
|
||||
{SIGTERM, SIG_TERM},
|
||||
{SIGCOMP1, SIG_IGN},
|
||||
{SIGCHLD, SIG_IGN},
|
||||
{SIGCONT, SIG_CONT},
|
||||
{SIGSTOP, SIG_STOP},
|
||||
{SIGTSTP, SIG_STOP},
|
||||
{SIGTTIN, SIG_STOP},
|
||||
{SIGTTOU, SIG_STOP},
|
||||
{SIGURG, SIG_IGN},
|
||||
{SIGXCPU, SIG_CORE},
|
||||
{SIGXFSZ, SIG_CORE},
|
||||
{SIGVTALRM, SIG_TERM},
|
||||
{SIGPROF, SIG_TERM},
|
||||
{SIGCOMP2, SIG_IGN},
|
||||
{SIGPOLL, SIG_TERM},
|
||||
{SIGCOMP3, SIG_IGN},
|
||||
{SIGSYS, SIG_CORE},
|
||||
{SIGRTMIN, SIG_IGN},
|
||||
{SIGRT_1, SIG_IGN},
|
||||
{SIGRT_2, SIG_IGN},
|
||||
{SIGRT_3, SIG_IGN},
|
||||
{SIGRT_4, SIG_IGN},
|
||||
{SIGRT_5, SIG_IGN},
|
||||
{SIGRT_6, SIG_IGN},
|
||||
{SIGRT_7, SIG_IGN},
|
||||
{SIGRT_8, SIG_IGN},
|
||||
{SIGRT_9, SIG_IGN},
|
||||
{SIGRT_10, SIG_IGN},
|
||||
{SIGRT_11, SIG_IGN},
|
||||
{SIGRT_12, SIG_IGN},
|
||||
{SIGRT_13, SIG_IGN},
|
||||
{SIGRT_14, SIG_IGN},
|
||||
{SIGRT_15, SIG_IGN},
|
||||
{SIGRT_16, SIG_IGN},
|
||||
{SIGRT_17, SIG_IGN},
|
||||
{SIGRT_18, SIG_IGN},
|
||||
{SIGRT_19, SIG_IGN},
|
||||
{SIGRT_20, SIG_IGN},
|
||||
{SIGRT_21, SIG_IGN},
|
||||
{SIGRT_22, SIG_IGN},
|
||||
{SIGRT_23, SIG_IGN},
|
||||
{SIGRT_24, SIG_IGN},
|
||||
{SIGRT_25, SIG_IGN},
|
||||
{SIGRT_26, SIG_IGN},
|
||||
{SIGRT_27, SIG_IGN},
|
||||
{SIGRT_28, SIG_IGN},
|
||||
{SIGRT_29, SIG_IGN},
|
||||
{SIGRT_30, SIG_IGN},
|
||||
{SIGRT_31, SIG_IGN},
|
||||
{SIGRTMAX, SIG_IGN},
|
||||
};
|
||||
|
||||
SignalDispositions GetDefaultSignalDisposition(Signals sig)
|
||||
{
|
||||
foreach (auto var in SignalDisposition)
|
||||
{
|
||||
if (var.Signal == sig)
|
||||
return var.Disposition;
|
||||
}
|
||||
|
||||
error("Invalid signal: %d", sig);
|
||||
return SIG_TERM;
|
||||
}
|
||||
|
||||
/* subsystem/linux/syscall.cpp */
|
||||
extern int ConvertSignalToLinux(Signals sig);
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
bool Signal::LinuxSig()
|
||||
{
|
||||
return ((PCB *)ctx)->Info.Compatibility == Linux;
|
||||
}
|
||||
|
||||
int Signal::MakeExitCode(Signals sig)
|
||||
{
|
||||
if (this->LinuxSig())
|
||||
return 128 + ConvertSignalToLinux(sig);
|
||||
else
|
||||
return 100 + sig;
|
||||
}
|
||||
|
||||
void Signal::InitTrampoline()
|
||||
{
|
||||
if (unlikely(TrampAddr))
|
||||
return;
|
||||
|
||||
PCB *pcb = (PCB *)ctx;
|
||||
|
||||
debug("Trampoline not set up yet");
|
||||
switch (pcb->Info.Compatibility)
|
||||
{
|
||||
case Native:
|
||||
{
|
||||
debug("%#lx - %#lx",
|
||||
&_sig_native_trampoline_end,
|
||||
&_sig_native_trampoline_start);
|
||||
|
||||
TrampSz = (size_t)&_sig_native_trampoline_end -
|
||||
(size_t)&_sig_native_trampoline_start;
|
||||
TrampAddr = pcb->vma->RequestPages(TO_PAGES(TrampSz), true);
|
||||
memcpy((void *)TrampAddr,
|
||||
(void *)&_sig_native_trampoline_start,
|
||||
TrampSz);
|
||||
debug("Trampoline at %#lx with size %lld",
|
||||
TrampAddr, TrampSz);
|
||||
break;
|
||||
}
|
||||
case Linux:
|
||||
{
|
||||
debug("%#lx - %#lx",
|
||||
&_sig_linux_trampoline_end,
|
||||
&_sig_linux_trampoline_start);
|
||||
|
||||
TrampSz = (size_t)&_sig_linux_trampoline_end -
|
||||
(size_t)&_sig_linux_trampoline_start;
|
||||
TrampAddr = pcb->vma->RequestPages(TO_PAGES(TrampSz), true);
|
||||
memcpy((void *)TrampAddr,
|
||||
(void *)&_sig_linux_trampoline_start,
|
||||
TrampSz);
|
||||
debug("Trampoline at %#lx with size %lld",
|
||||
TrampAddr, TrampSz);
|
||||
break;
|
||||
}
|
||||
case Windows:
|
||||
{
|
||||
assert(!"Windows compatibility not implemented");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Process not fully initalized. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
|
||||
int Signal::AddWatcher(Signal *who, Signals sig)
|
||||
{
|
||||
SignalInfo info;
|
||||
info.sig = sig;
|
||||
info.val.sival_ptr = who;
|
||||
|
||||
SmartLock(SignalLock);
|
||||
Watchers.push_back(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::RemoveWatcher(Signal *who, Signals sig)
|
||||
{
|
||||
SmartLock(SignalLock);
|
||||
forItr(itr, Watchers)
|
||||
{
|
||||
if (itr->sig == sig &&
|
||||
itr->val.sival_ptr == who)
|
||||
{
|
||||
Watchers.erase(itr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int Signal::AddSignal(Signals sig, union sigval val, pid_t tid)
|
||||
{
|
||||
SignalInfo info{.sig = sig, .val = val, .tid = tid};
|
||||
Queue.push_back(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::RemoveSignal(Signals sig)
|
||||
{
|
||||
size_t n = Queue.remove_if([sig](SignalInfo &info)
|
||||
{ return info.sig == sig; });
|
||||
debug("Removed %d signals", n);
|
||||
return n ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
Signal::SignalInfo Signal::GetAvailableSignal(void *thread)
|
||||
{
|
||||
forItr(itr, Queue)
|
||||
{
|
||||
// if (GlobalMask.test(itr->sig - 1))
|
||||
// {
|
||||
// debug("Signal %s is blocked by global mask",
|
||||
// sigStr[itr->sig]);
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (((TCB *)thread)->Signals.Mask.test(itr->sig - 1))
|
||||
{
|
||||
debug("Signal %s is blocked by thread mask",
|
||||
sigStr[itr->sig]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (((TCB *)thread)->ID != itr->tid && itr->tid != -1)
|
||||
{
|
||||
debug("Signal %s is not for this thread",
|
||||
sigStr[itr->sig]);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(sa[itr->sig].sa_handler.Disposition != SAD_IGN);
|
||||
assert(sa[itr->sig].sa_handler.Disposition != SAD_DFL);
|
||||
|
||||
Queue.erase(itr);
|
||||
debug("Signal %s is available", sigStr[itr->sig]);
|
||||
return *itr;
|
||||
}
|
||||
|
||||
debug("No signal available");
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Signal::HandleSignal(CPU::SchedulerFrame *tf, void *thread)
|
||||
{
|
||||
/* We don't want to do this in kernel mode */
|
||||
if (tf->cs != GDT_USER_CODE)
|
||||
return false;
|
||||
|
||||
if (Queue.empty())
|
||||
return false;
|
||||
|
||||
debug("We have %d signals to handle", Queue.size());
|
||||
|
||||
SmartLock(SignalLock);
|
||||
SignalInfo sigI = GetAvailableSignal(thread);
|
||||
if (sigI.sig == SIG_NULL)
|
||||
return false;
|
||||
|
||||
uintptr_t _p_rsp = ((PCB *)ctx)->PageTable->Get(tf->rsp);
|
||||
uint64_t paRsp = _p_rsp;
|
||||
paRsp &= ~0xF; /* Align */
|
||||
paRsp -= 128; /* Red zone */
|
||||
|
||||
/* Calculate the virtual rsp */
|
||||
uintptr_t _v_rsp = tf->rsp;
|
||||
_v_rsp &= ~0xF; /* Align */
|
||||
_v_rsp -= 128; /* Red zone */
|
||||
uint64_t *vRsp = (uint64_t *)(_v_rsp - sizeof(StackInfo));
|
||||
vRsp--; /* Alignment */
|
||||
vRsp--; /* Handler Address */
|
||||
assert(!((uintptr_t)vRsp & 0xF));
|
||||
|
||||
/* Add the stack info */
|
||||
StackInfo si{};
|
||||
CPU::x64::fxsave(&si.fx);
|
||||
si.tf = *tf;
|
||||
si.GSBase = CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE);
|
||||
si.FSBase = CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE);
|
||||
si.ShadowGSBase = CPU::x64::rdmsr(CPU::x64::MSR_SHADOW_GS_BASE);
|
||||
si.SignalMask = ((TCB *)thread)->Signals.Mask.to_ulong();
|
||||
si.Compatibility = ((PCB *)ctx)->Info.Compatibility;
|
||||
|
||||
debug("gs: %#lx fs: %#lx shadow: %#lx",
|
||||
si.GSBase, si.FSBase, si.ShadowGSBase);
|
||||
|
||||
/* Copy the stack info */
|
||||
uint64_t *pRsp = (uint64_t *)(paRsp - sizeof(StackInfo));
|
||||
memcpy(pRsp, &si, sizeof(StackInfo));
|
||||
|
||||
/* Set the handler address */
|
||||
pRsp--; /* Alignment */
|
||||
pRsp--;
|
||||
*pRsp = uint64_t(sa[sigI.sig].sa_handler.Handler);
|
||||
|
||||
assert(!((uintptr_t)pRsp & 0xF));
|
||||
|
||||
int cSig = LinuxSig() ? ConvertSignalToLinux((Signals)sigI.sig) : sigI.sig;
|
||||
|
||||
#ifdef DEBUG
|
||||
DumpData("Stack Data", (void *)pRsp,
|
||||
paRsp - uint64_t(pRsp));
|
||||
debug("initial stack tf->rsp: %#lx after: %#lx",
|
||||
tf->rsp, uint64_t(vRsp));
|
||||
debug("sig: %d -> %d", sigI.sig, cSig);
|
||||
#endif
|
||||
|
||||
tf->rsp = uint64_t(vRsp);
|
||||
tf->rip = uint64_t(TrampAddr);
|
||||
|
||||
/* void func(int signo); */
|
||||
/* void func(int signo, siginfo_t *info, void *context); */
|
||||
tf->rdi = cSig;
|
||||
if (sa[sigI.sig].Flags & SA_SIGINFO)
|
||||
{
|
||||
fixme("SA_SIGINFO not implemented");
|
||||
siginfo_t *info = 0;
|
||||
void *context = 0;
|
||||
tf->rsi = uint64_t(info);
|
||||
tf->rdx = uint64_t(context);
|
||||
tf->rcx = 0;
|
||||
tf->r8 = 0;
|
||||
tf->r9 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
tf->rsi = 0;
|
||||
tf->rdx = 0;
|
||||
tf->rcx = 0;
|
||||
tf->r8 = 0;
|
||||
tf->r9 = 0;
|
||||
}
|
||||
|
||||
((TCB *)thread)->Signals.Mask = sa[sigI.sig].Mask;
|
||||
assert(TrampAddr != nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Signal::RestoreHandleSignal(SyscallsFrame *sf, void *thread)
|
||||
{
|
||||
debug("Restoring signal handler");
|
||||
SmartLock(SignalLock);
|
||||
|
||||
gsTCB *gs = (gsTCB *)CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE);
|
||||
uint64_t *sp = (uint64_t *)((PCB *)ctx)->PageTable->Get(gs->TempStack);
|
||||
sp++; /* Alignment */
|
||||
sp++; /* Handler Address */
|
||||
|
||||
assert(!((uintptr_t)sp & 0xF));
|
||||
|
||||
StackInfo *si = (StackInfo *)sp;
|
||||
assert(si != nullptr);
|
||||
|
||||
sf->r15 = si->tf.r15;
|
||||
sf->r14 = si->tf.r14;
|
||||
sf->r13 = si->tf.r13;
|
||||
sf->r12 = si->tf.r12;
|
||||
sf->r11 = si->tf.r11;
|
||||
sf->r10 = si->tf.r10;
|
||||
sf->r9 = si->tf.r9;
|
||||
sf->r8 = si->tf.r8;
|
||||
sf->rbp = si->tf.rbp;
|
||||
sf->rdi = si->tf.rdi;
|
||||
sf->rsi = si->tf.rsi;
|
||||
sf->rdx = si->tf.rdx;
|
||||
sf->rcx = si->tf.rcx;
|
||||
sf->rbx = si->tf.rbx;
|
||||
sf->rax = si->tf.rax;
|
||||
sf->Flags = si->tf.rflags.raw;
|
||||
sf->ReturnAddress = si->tf.rip;
|
||||
gs->TempStack = (void *)si->tf.rsp;
|
||||
|
||||
((TCB *)thread)->Signals.Mask = si->SignalMask;
|
||||
|
||||
CPU::x64::fxrstor(&si->fx);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, si->ShadowGSBase);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_FS_BASE, si->FSBase);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, si->GSBase);
|
||||
debug("gs: %#lx fs: %#lx shadow: %#lx",
|
||||
si->GSBase, si->FSBase, si->ShadowGSBase);
|
||||
|
||||
// ((PCB *)ctx)->GetContext()->Yield();
|
||||
// __builtin_unreachable();
|
||||
/* Return because we will restore at sysretq */
|
||||
}
|
||||
|
||||
int Signal::SetAction(Signals sig, const SignalAction *act)
|
||||
{
|
||||
SmartLock(SignalLock);
|
||||
if ((size_t)sig > sizeof(sa) / sizeof(sa[0]))
|
||||
{
|
||||
debug("Invalid signal: %d", sig);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((long)act->sa_handler.Disposition == SAD_IGN)
|
||||
{
|
||||
Disposition[sig] = SIG_IGN;
|
||||
debug("Set disposition for %s to SIG_IGN", sigStr[sig]);
|
||||
debug("Discarding pending signals %s", sigStr[sig]);
|
||||
|
||||
bool found = false;
|
||||
forItr(itr, Queue)
|
||||
{
|
||||
if (itr->sig == sig)
|
||||
{
|
||||
Queue.erase(itr);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
debug("No pending signal %s", sigStr[sig]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((long)act->sa_handler.Disposition == SAD_DFL)
|
||||
{
|
||||
Disposition[sig] = GetDefaultSignalDisposition(sig);
|
||||
debug("Set disposition for %s to %s (default)", sigStr[sig],
|
||||
dispStr[Disposition[sig]]);
|
||||
}
|
||||
|
||||
sa[sig].sa_handler.Handler = act->sa_handler.Handler;
|
||||
sa[sig].Mask = act->Mask;
|
||||
sa[sig].Flags = act->Flags;
|
||||
sa[sig].Restorer = act->Restorer;
|
||||
|
||||
debug("Set action for %s with handler %#lx, mask %#lx and flags %#lx",
|
||||
sigStr[sig], sa[sig].sa_handler.Handler, sa[sig].Mask, sa[sig].Flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::GetAction(Signals sig, SignalAction *act)
|
||||
{
|
||||
SmartLock(SignalLock);
|
||||
if ((size_t)sig > sizeof(sa) / sizeof(sa[0]))
|
||||
{
|
||||
debug("Invalid signal: %d", sig);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
act->sa_handler.Handler = sa[sig].sa_handler.Handler;
|
||||
act->Mask = sa[sig].Mask;
|
||||
act->Flags = sa[sig].Flags;
|
||||
act->Restorer = sa[sig].Restorer;
|
||||
debug("Got action for %s with handler %#lx, mask %#lx and flags %#lx",
|
||||
sigStr[sig], sa[sig].sa_handler.Handler, sa[sig].Mask, sa[sig].Flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::SendSignal(Signals sig, sigval val, pid_t tid)
|
||||
{
|
||||
SmartLock(SignalLock);
|
||||
PCB *pcb = (PCB *)ctx;
|
||||
LastSignal = sig;
|
||||
|
||||
debug("Sending signal %s to %s(%d)",
|
||||
sigStr[sig], pcb->Name, pcb->ID);
|
||||
|
||||
if (sa[sig].sa_handler.Handler)
|
||||
{
|
||||
if (pcb->Security.ExecutionMode == Kernel)
|
||||
{
|
||||
info("Kernel processes cannot have signal handlers");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sa[sig].sa_handler.Disposition == SAD_IGN)
|
||||
{
|
||||
debug("Ignoring signal %s", sigStr[sig]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("sa_handler: %#lx", sa[sig].sa_handler.Handler);
|
||||
debug("Adding signal %s to queue", sigStr[sig]);
|
||||
goto CompleteSignal;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("No handler for signal %s", sigStr[sig]);
|
||||
}
|
||||
|
||||
if (thisThread->Signals.Mask.test(sig - 1))
|
||||
{
|
||||
debug("Signal %s is blocked by thread mask",
|
||||
sigStr[sig]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("Signal disposition: %s", dispStr[Disposition[sig]]);
|
||||
switch (Disposition[sig])
|
||||
{
|
||||
case SIG_TERM:
|
||||
{
|
||||
if (unlikely(pcb->Security.IsCritical))
|
||||
{
|
||||
debug("Critical process %s received signal %s(%d): Terminated",
|
||||
pcb->Name, sigStr[sig], sig);
|
||||
// int3;
|
||||
}
|
||||
|
||||
pcb->SetExitCode(MakeExitCode(sig));
|
||||
debug("We have %d watchers", this->Watchers.size());
|
||||
if (this->Watchers.size() > 0)
|
||||
pcb->SetState(Zombie);
|
||||
else
|
||||
pcb->SetState(Terminated);
|
||||
return val.sival_int == Tasking::KILL_CRASH ? -EFAULT : 0;
|
||||
}
|
||||
case SIG_IGN:
|
||||
{
|
||||
debug("Ignoring signal %d", sig);
|
||||
return 0;
|
||||
}
|
||||
case SIG_CORE:
|
||||
{
|
||||
fixme("core dump");
|
||||
|
||||
if (unlikely(pcb->Security.IsCritical))
|
||||
{
|
||||
debug("Critical process %s received signal %s(%d): Core dumped",
|
||||
pcb->Name, sigStr[sig], sig);
|
||||
// int3;
|
||||
}
|
||||
|
||||
pcb->SetExitCode(MakeExitCode(sig));
|
||||
debug("We have %d watchers", this->Watchers.size());
|
||||
if (this->Watchers.size() > 0)
|
||||
pcb->SetState(CoreDump);
|
||||
else
|
||||
pcb->SetState(Terminated);
|
||||
return val.sival_int == Tasking::KILL_CRASH ? -EFAULT : 0;
|
||||
}
|
||||
case SIG_STOP:
|
||||
{
|
||||
debug("Stopping process %s(%d) with signal %s(%d)",
|
||||
pcb->Name, pcb->ID, sigStr[sig], sig);
|
||||
pcb->SetState(Stopped);
|
||||
return 0;
|
||||
}
|
||||
case SIG_CONT:
|
||||
{
|
||||
debug("Continuing process %s(%d) with signal %s(%d)",
|
||||
pcb->Name, pcb->ID, sigStr[sig], sig);
|
||||
pcb->SetState(Ready);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid signal disposition");
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
CompleteSignal:
|
||||
if (pcb->Security.ExecutionMode == Kernel)
|
||||
{
|
||||
debug("Kernel process %s received signal %s(%d)! Ignoring... (with exceptions)",
|
||||
pcb->Name, sigStr[sig], sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->InitTrampoline();
|
||||
|
||||
debug("Signal %s(%d) completed", sigStr[sig], sig);
|
||||
if (Disposition[sig] != SIG_IGN)
|
||||
{
|
||||
foreach (auto info in Watchers)
|
||||
{
|
||||
Signal *who = (Signal *)info.val.sival_ptr;
|
||||
assert(who != nullptr);
|
||||
debug("Sending SIGCHLD to %s(%d)",
|
||||
((PCB *)who->GetContext())->Name,
|
||||
((PCB *)who->GetContext())->ID);
|
||||
who->SendSignal(SIGCHLD, val);
|
||||
}
|
||||
}
|
||||
|
||||
debug("Adding signal to queue");
|
||||
Queue.push_back({.sig = sig, .val = val, .tid = tid});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::WaitAnySignal()
|
||||
{
|
||||
debug("Waiting for any signal");
|
||||
|
||||
size_t oldSize = Queue.size();
|
||||
Reset:
|
||||
while (Queue.size() == oldSize)
|
||||
TaskManager->Yield();
|
||||
|
||||
if (Queue.size() > oldSize)
|
||||
{
|
||||
debug("Added signal to queue %d > %d",
|
||||
Queue.size(), oldSize);
|
||||
oldSize = Queue.size();
|
||||
goto Reset;
|
||||
}
|
||||
|
||||
debug("Signal received");
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
int Signal::WaitSignal(Signals sig, union sigval *val)
|
||||
{
|
||||
assert(!"WaitSignal not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::WaitSignalTimeout(Signals sig, union sigval *val, uint64_t timeout)
|
||||
{
|
||||
assert(!"WaitSignalTimeout not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Signal::Signal(void *_ctx)
|
||||
{
|
||||
assert(_ctx != nullptr);
|
||||
this->ctx = _ctx;
|
||||
|
||||
// for (int i = 1; i < SIGNAL_MAX; i++)
|
||||
// Disposition[i] = GetDefaultSignalDisposition(i);
|
||||
|
||||
#ifdef DEBUG
|
||||
static int once = 0;
|
||||
if (!once++)
|
||||
{
|
||||
for (int i = 1; i < SIGNAL_MAX; i++)
|
||||
debug("%s: %s",
|
||||
sigStr[i],
|
||||
dispStr[Disposition[(Signals)i]]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Signal::~Signal() {}
|
||||
|
||||
sigset_t ThreadSignal::Block(sigset_t sig)
|
||||
{
|
||||
sigset_t oldMask = Mask.to_ulong();
|
||||
Mask |= sig;
|
||||
debug("%#lx -> %#lx", oldMask, Mask);
|
||||
return oldMask;
|
||||
}
|
||||
|
||||
sigset_t ThreadSignal::Unblock(sigset_t sig)
|
||||
{
|
||||
sigset_t oldMask = Mask.to_ulong();
|
||||
Mask &= ~sig;
|
||||
debug("%#lx -> %#lx", oldMask, Mask);
|
||||
return oldMask;
|
||||
}
|
||||
|
||||
sigset_t ThreadSignal::SetMask(sigset_t sig)
|
||||
{
|
||||
sigset_t oldMask = Mask.to_ulong();
|
||||
Mask = sig;
|
||||
debug("%#lx -> %#lx", oldMask, Mask);
|
||||
return oldMask;
|
||||
}
|
||||
|
||||
sigset_t ThreadSignal::GetMask()
|
||||
{
|
||||
return Mask.to_ulong();
|
||||
}
|
||||
}
|
281
Kernel/tasking/task.cpp
Normal file
281
Kernel/tasking/task.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
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 <scheduler.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 "../arch/amd64/cpu/apic.hpp"
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/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
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
PCB *Task::GetCurrentProcess()
|
||||
{
|
||||
return GetCurrentCPU()->CurrentProcess.load();
|
||||
}
|
||||
|
||||
TCB *Task::GetCurrentThread()
|
||||
{
|
||||
return GetCurrentCPU()->CurrentThread.load();
|
||||
}
|
||||
|
||||
PCB *Task::GetProcessByID(TID ID)
|
||||
{
|
||||
return ((Scheduler::Base *)Scheduler)->GetProcessByID(ID);
|
||||
}
|
||||
|
||||
TCB *Task::GetThreadByID(TID ID, PCB *Parent)
|
||||
{
|
||||
return ((Scheduler::Base *)Scheduler)->GetThreadByID(ID, Parent);
|
||||
}
|
||||
|
||||
std::vector<PCB *> Task::GetProcessList()
|
||||
{
|
||||
return ((Scheduler::Base *)Scheduler)->GetProcessList();
|
||||
}
|
||||
|
||||
void Task::Panic()
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->StopScheduler.store(true);
|
||||
}
|
||||
|
||||
bool Task::IsPanic()
|
||||
{
|
||||
return ((Scheduler::Base *)Scheduler)->StopScheduler;
|
||||
}
|
||||
|
||||
void Task::Yield()
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->Yield();
|
||||
}
|
||||
|
||||
void Task::UpdateFrame()
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->SchedulerUpdateTrapFrame = true;
|
||||
((Scheduler::Base *)Scheduler)->Yield();
|
||||
}
|
||||
|
||||
void Task::PushProcess(PCB *pcb)
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->PushProcess(pcb);
|
||||
}
|
||||
|
||||
void Task::PopProcess(PCB *pcb)
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->PopProcess(pcb);
|
||||
}
|
||||
|
||||
void Task::WaitForProcess(PCB *pcb)
|
||||
{
|
||||
if (pcb == nullptr)
|
||||
return;
|
||||
|
||||
if (pcb->State == TaskState::UnknownStatus)
|
||||
return;
|
||||
|
||||
debug("Waiting for process \"%s\"(%d)",
|
||||
pcb->Name, pcb->ID);
|
||||
|
||||
while (pcb->State != TaskState::Terminated &&
|
||||
pcb->State != TaskState::Zombie &&
|
||||
pcb->State != TaskState::CoreDump)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::WaitForThread(TCB *tcb)
|
||||
{
|
||||
if (tcb == nullptr)
|
||||
return;
|
||||
|
||||
if (tcb->State == TaskState::UnknownStatus)
|
||||
return;
|
||||
|
||||
debug("Waiting for thread \"%s\"(%d)",
|
||||
tcb->Name, tcb->ID);
|
||||
|
||||
while (tcb->State != TaskState::Terminated &&
|
||||
tcb->State != TaskState::Zombie &&
|
||||
tcb->State != TaskState::CoreDump)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::WaitForProcessStatus(PCB *pcb, TaskState status)
|
||||
{
|
||||
if (pcb == nullptr)
|
||||
return;
|
||||
|
||||
if (pcb->State == TaskState::UnknownStatus)
|
||||
return;
|
||||
|
||||
debug("Waiting for process \"%s\"(%d) to reach status: %d",
|
||||
pcb->Name, pcb->ID, status);
|
||||
|
||||
while (pcb->State != status)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::WaitForThreadStatus(TCB *tcb, TaskState status)
|
||||
{
|
||||
if (tcb == nullptr)
|
||||
return;
|
||||
|
||||
if (tcb->State == TaskState::UnknownStatus)
|
||||
return;
|
||||
|
||||
debug("Waiting for thread \"%s\"(%d) to reach status: %d",
|
||||
tcb->Name, tcb->ID, status);
|
||||
|
||||
while (tcb->State != status)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::Sleep(uint64_t Milliseconds, bool NoSwitch)
|
||||
{
|
||||
TCB *thread = this->GetCurrentThread();
|
||||
PCB *process = thread->Parent;
|
||||
|
||||
thread->SetState(TaskState::Sleeping);
|
||||
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
if (process->Threads.size() == 1)
|
||||
process->SetState(TaskState::Sleeping);
|
||||
|
||||
thread->Info.SleepUntil =
|
||||
TimeManager->CalculateTarget(Milliseconds,
|
||||
Time::Units::Milliseconds);
|
||||
}
|
||||
|
||||
// #ifdef DEBUG
|
||||
// uint64_t TicksNow = TimeManager->GetCounter();
|
||||
// #endif
|
||||
// debug("Thread \"%s\"(%d) is going to sleep until %llu, current %llu, diff %llu",
|
||||
// thread->Name, thread->ID, thread->Info.SleepUntil,
|
||||
// TicksNow, thread->Info.SleepUntil - TicksNow);
|
||||
|
||||
if (!NoSwitch)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::SignalShutdown()
|
||||
{
|
||||
debug("Current process is %s(%d) and thread is %s(%d)",
|
||||
GetCurrentProcess()->Name, GetCurrentProcess()->ID,
|
||||
GetCurrentThread()->Name, GetCurrentThread()->ID);
|
||||
|
||||
foreach (auto pcb in((Scheduler::Base *)Scheduler)->GetProcessList())
|
||||
{
|
||||
if (pcb->State == TaskState::Terminated ||
|
||||
pcb->State == TaskState::Zombie)
|
||||
continue;
|
||||
|
||||
if (pcb == GetCurrentProcess())
|
||||
continue;
|
||||
|
||||
debug("Sending SIGTERM to process \"%s\"(%d)",
|
||||
pcb->Name, pcb->ID);
|
||||
pcb->SendSignal(SIGTERM);
|
||||
}
|
||||
|
||||
// TODO: wait for processes to terminate with timeout.
|
||||
}
|
||||
|
||||
__no_sanitize("undefined")
|
||||
TCB *Task::CreateThread(PCB *Parent, IP EntryPoint,
|
||||
const char **argv, const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv,
|
||||
TaskArchitecture arch, TaskCompatibility Compatibility,
|
||||
bool ThreadNotReady)
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
return new TCB(this, Parent, EntryPoint,
|
||||
argv, envp, auxv, arch,
|
||||
Compatibility, ThreadNotReady);
|
||||
}
|
||||
|
||||
PCB *Task::CreateProcess(PCB *Parent, const char *Name,
|
||||
TaskExecutionMode ExecutionMode, bool UseKernelPageTable,
|
||||
uint16_t UserID, uint16_t GroupID)
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
return new PCB(this, Parent, Name, ExecutionMode,
|
||||
UseKernelPageTable, UserID, GroupID);
|
||||
}
|
||||
|
||||
void Task::StartScheduler()
|
||||
{
|
||||
((Scheduler::Base *)Scheduler)->StartScheduler();
|
||||
debug("Tasking Started");
|
||||
}
|
||||
|
||||
Task::Task(const IP EntryPoint)
|
||||
{
|
||||
/* I don't know if this is the best way to do this. */
|
||||
Scheduler::Custom *custom_sched = new Scheduler::Custom(this);
|
||||
Scheduler::Base *sched = r_cst(Scheduler::Base *, custom_sched);
|
||||
__sched_ctx = custom_sched;
|
||||
Scheduler = sched;
|
||||
|
||||
KernelProcess = CreateProcess(nullptr, "Kernel",
|
||||
TaskExecutionMode::Kernel, true);
|
||||
KernelProcess->PageTable = KernelPageTable;
|
||||
TCB *kthrd = CreateThread(KernelProcess, EntryPoint,
|
||||
nullptr, nullptr,
|
||||
std::vector<AuxiliaryVector>(), GetKArch());
|
||||
kthrd->Rename("Main Thread");
|
||||
debug("Created Kernel Process: %s and Thread: %s",
|
||||
KernelProcess->Name, kthrd->Name);
|
||||
|
||||
if (!CPU::Interrupts(CPU::Check))
|
||||
{
|
||||
error("Interrupts are not enabled.");
|
||||
CPU::Interrupts(CPU::Enable);
|
||||
}
|
||||
|
||||
((Scheduler::Base *)Scheduler)->StartIdleProcess();
|
||||
debug("Tasking is ready");
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
delete (Scheduler::Custom *)__sched_ctx;
|
||||
}
|
||||
}
|
681
Kernel/tasking/thread.cpp
Normal file
681
Kernel/tasking/thread.cpp
Normal file
@ -0,0 +1,681 @@
|
||||
/*
|
||||
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 <filesystem/ioctl.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 "../arch/amd64/cpu/apic.hpp"
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/i386/cpu/apic.hpp"
|
||||
#include "../arch/i386/cpu/gdt.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
|
||||
|
||||
/* For kernel threads only */
|
||||
void ThreadDoExit()
|
||||
{
|
||||
CPUData *CPUData = GetCurrentCPU();
|
||||
Tasking::TCB *CurrentThread = CPUData->CurrentThread.load();
|
||||
CurrentThread->SetState(Tasking::Terminated);
|
||||
|
||||
debug("\"%s\"(%d) exited with code: %#lx",
|
||||
CurrentThread->Name,
|
||||
CurrentThread->ID,
|
||||
CurrentThread->ExitCode);
|
||||
CPU::Halt(true);
|
||||
}
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
int TCB::SendSignal(int sig)
|
||||
{
|
||||
return this->Parent->Signals.SendSignal((enum Signals)sig, {0}, this->ID);
|
||||
}
|
||||
|
||||
void TCB::SetState(TaskState state)
|
||||
{
|
||||
this->State.store(state);
|
||||
if (this->Parent->Threads.size() == 1)
|
||||
this->Parent->State.store(state);
|
||||
}
|
||||
|
||||
void TCB::SetExitCode(int code)
|
||||
{
|
||||
this->ExitCode.store(code);
|
||||
if (this->Parent->Threads.size() == 1)
|
||||
this->Parent->ExitCode.store(code);
|
||||
}
|
||||
|
||||
void TCB::Rename(const char *name)
|
||||
{
|
||||
assert(name != nullptr);
|
||||
assert(strlen(name) > 0);
|
||||
|
||||
trace("Renaming thread %s to %s",
|
||||
this->Name, name);
|
||||
|
||||
if (this->Name)
|
||||
{
|
||||
this->AllocatedMemory -= strlen(this->Name) + 1;
|
||||
delete[] this->Name;
|
||||
}
|
||||
|
||||
this->Name = new char[strlen(name) + 1];
|
||||
this->AllocatedMemory += strlen(name) + 1;
|
||||
strcpy((char *)this->Name, name);
|
||||
}
|
||||
|
||||
void TCB::SetPriority(TaskPriority priority)
|
||||
{
|
||||
assert(priority >= _PriorityMin);
|
||||
// assert(priority <= _PriorityMax);
|
||||
|
||||
trace("Setting priority of thread %s to %d",
|
||||
this->Name, priority);
|
||||
|
||||
Info.Priority = priority;
|
||||
}
|
||||
|
||||
void TCB::SetCritical(bool Critical)
|
||||
{
|
||||
trace("Setting criticality of thread %s to %s",
|
||||
this->Name, Critical ? "true" : "false");
|
||||
|
||||
Security.IsCritical = Critical;
|
||||
|
||||
if (this->Parent->Threads.size() == 1)
|
||||
this->Parent->Security.IsCritical = Critical;
|
||||
}
|
||||
|
||||
void TCB::SetDebugMode(bool Enable)
|
||||
{
|
||||
trace("Setting debug mode of thread %s to %s",
|
||||
this->Name, Enable ? "true" : "false");
|
||||
|
||||
Security.IsDebugEnabled = Enable;
|
||||
|
||||
if (this->Parent->Threads.size() == 1)
|
||||
this->Parent->Security.IsDebugEnabled = Enable;
|
||||
}
|
||||
|
||||
void TCB::SetKernelDebugMode(bool Enable)
|
||||
{
|
||||
trace("Setting kernel debug mode of thread %s to %s",
|
||||
this->Name, Enable ? "true" : "false");
|
||||
|
||||
Security.IsKernelDebugEnabled = Enable;
|
||||
|
||||
if (this->Parent->Threads.size() == 1)
|
||||
this->Parent->Security.IsKernelDebugEnabled = Enable;
|
||||
}
|
||||
|
||||
size_t TCB::GetSize()
|
||||
{
|
||||
size_t ret = this->AllocatedMemory;
|
||||
ret += this->vma->GetAllocatedMemorySize();
|
||||
ret += this->Stack->GetSize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TCB::SYSV_ABI_Call(uintptr_t Arg1, uintptr_t Arg2,
|
||||
uintptr_t Arg3, uintptr_t Arg4,
|
||||
uintptr_t Arg5, uintptr_t Arg6,
|
||||
void *Function)
|
||||
{
|
||||
#if defined(a64)
|
||||
this->Registers.rdi = Arg1;
|
||||
this->Registers.rsi = Arg2;
|
||||
this->Registers.rdx = Arg3;
|
||||
this->Registers.rcx = Arg4;
|
||||
this->Registers.r8 = Arg5;
|
||||
this->Registers.r9 = Arg6;
|
||||
if (Function != nullptr)
|
||||
this->Registers.rip = (uint64_t)Function;
|
||||
#elif defined(a32)
|
||||
this->Registers.eax = Arg1;
|
||||
this->Registers.ebx = Arg2;
|
||||
this->Registers.ecx = Arg3;
|
||||
this->Registers.edx = Arg4;
|
||||
this->Registers.esi = Arg5;
|
||||
this->Registers.edi = Arg6;
|
||||
if (Function != nullptr)
|
||||
this->Registers.eip = (uint32_t)Function;
|
||||
#else
|
||||
#warning "SYSV ABI not implemented for this architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
__no_sanitize("undefined") void TCB::SetupUserStack_x86_64(const char **argv,
|
||||
const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv,
|
||||
TaskCompatibility Compatibility)
|
||||
{
|
||||
size_t argvLen = 0;
|
||||
if (argv)
|
||||
while (argv[argvLen] != nullptr)
|
||||
argvLen++;
|
||||
|
||||
size_t envpLen = 0;
|
||||
if (envp)
|
||||
while (envp[envpLen] != nullptr)
|
||||
envpLen++;
|
||||
|
||||
debug("argvLen: %d", argvLen);
|
||||
debug("envpLen: %d", envpLen);
|
||||
|
||||
/* 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 *pStack = (char *)this->Stack->GetStackPhysicalTop();
|
||||
/* Temporary stack pointer for strings */
|
||||
char *stackStr = (char *)pStack;
|
||||
char *stackStrVirtual = (char *)this->Stack->GetStackTop();
|
||||
|
||||
/* Store string pointers for later */
|
||||
uintptr_t *argvStrings = nullptr;
|
||||
uintptr_t *envpStrings = nullptr;
|
||||
if (argvLen > 0)
|
||||
argvStrings = new uintptr_t[argvLen];
|
||||
if (envpLen > 0)
|
||||
envpStrings = new uintptr_t[envpLen];
|
||||
|
||||
for (size_t i = 0; i < argvLen; i++)
|
||||
{
|
||||
/* Subtract the length of the string and the null terminator */
|
||||
stackStr -= strlen(argv[i]) + 1;
|
||||
stackStrVirtual -= strlen(argv[i]) + 1;
|
||||
/* Store the pointer to the string */
|
||||
argvStrings[i] = (uintptr_t)stackStrVirtual;
|
||||
/* Copy the string to the stack */
|
||||
strcpy(stackStr, argv[i]);
|
||||
debug("argv[%d]: %s", i, argv[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < envpLen; i++)
|
||||
{
|
||||
/* Subtract the length of the string and the null terminator */
|
||||
stackStr -= strlen(envp[i]) + 1;
|
||||
stackStrVirtual -= strlen(envp[i]) + 1;
|
||||
/* Store the pointer to the string */
|
||||
envpStrings[i] = (uintptr_t)stackStrVirtual;
|
||||
/* Copy the string to the stack */
|
||||
strcpy(stackStr, envp[i]);
|
||||
debug("envp[%d]: %s", i, envp[i]);
|
||||
}
|
||||
|
||||
/* Align the stack to 16 bytes */
|
||||
stackStr -= (uintptr_t)stackStr & 0xF;
|
||||
/* Set "pStack" to the new stack pointer */
|
||||
pStack = (char *)stackStr;
|
||||
/* If argv and envp sizes are odd then we need to align the stack */
|
||||
pStack -= (argvLen + envpLen) % 2;
|
||||
debug("odd align: %#lx | %#lx -> %#lx",
|
||||
(argvLen + envpLen) % 2,
|
||||
stackStr, pStack);
|
||||
|
||||
/* Ensure StackPointerReg is aligned to the closest lower 16 bytes boundary */
|
||||
uintptr_t lower16Align = (uintptr_t)pStack;
|
||||
lower16Align &= ~0xF;
|
||||
debug("before: %#lx ; after: %#lx", pStack, lower16Align);
|
||||
pStack = (char *)lower16Align;
|
||||
|
||||
/* We need 8 bit pointers for the stack from here */
|
||||
uintptr_t *Stack64 = (uintptr_t *)pStack;
|
||||
assert(Stack64 != nullptr);
|
||||
|
||||
/* Store the null terminator */
|
||||
StackPush(Stack64, uintptr_t, 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 av in auxv_array)
|
||||
{
|
||||
/* Subtract the size of the auxillary vector */
|
||||
Stack64 -= sizeof(Elf_auxv_t) / sizeof(uintptr_t);
|
||||
/* Store the auxillary vector */
|
||||
POKE(Elf_auxv_t, Stack64) = av.archaux;
|
||||
/* TODO: Store strings to the stack */
|
||||
}
|
||||
|
||||
/* Store the null terminator */
|
||||
StackPush(Stack64, uintptr_t, AT_NULL);
|
||||
|
||||
/* Store envpStrings[] to the stack */
|
||||
Stack64 -= envpLen; /* (1 Stack64 = 8 bits; Stack64 = 8 * envpLen) */
|
||||
for (size_t i = 0; i < envpLen; i++)
|
||||
{
|
||||
*(Stack64 + i) = (uintptr_t)envpStrings[i];
|
||||
debug("envpStrings[%d]: %#lx",
|
||||
i, envpStrings[i]);
|
||||
}
|
||||
|
||||
/* Store the null terminator */
|
||||
StackPush(Stack64, uintptr_t, AT_NULL);
|
||||
|
||||
/* Store argvStrings[] to the stack */
|
||||
Stack64 -= argvLen; /* (1 Stack64 = 8 bits; Stack64 = 8 * argvLen) */
|
||||
for (size_t i = 0; i < argvLen; i++)
|
||||
{
|
||||
*(Stack64 + i) = (uintptr_t)argvStrings[i];
|
||||
debug("argvStrings[%d]: %#lx",
|
||||
i, argvStrings[i]);
|
||||
}
|
||||
|
||||
/* Store the argc */
|
||||
StackPush(Stack64, uintptr_t, argvLen);
|
||||
|
||||
/* Set "pStack" to the new stack pointer */
|
||||
pStack = (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)this->Stack->GetStackPhysicalTop() - (uintptr_t)pStack;
|
||||
debug("SubtractStack: %#lx", SubtractStack);
|
||||
|
||||
/* Set the stack pointer to the new stack */
|
||||
uintptr_t StackPointerReg = ((uintptr_t)this->Stack->GetStackTop() - SubtractStack);
|
||||
// assert(!(StackPointerReg & 0xF));
|
||||
|
||||
#if defined(a64)
|
||||
this->Registers.rsp = StackPointerReg;
|
||||
#elif defined(a32)
|
||||
this->Registers.esp = StackPointerReg;
|
||||
#elif defined(aa64)
|
||||
this->Registers.sp = StackPointerReg;
|
||||
#endif
|
||||
|
||||
if (argvLen > 0)
|
||||
delete[] argvStrings;
|
||||
if (envpLen > 0)
|
||||
delete[] envpStrings;
|
||||
|
||||
#ifdef DEBUG
|
||||
DumpData("Stack Data",
|
||||
(void *)((uintptr_t)this->Stack->GetStackPhysicalTop() - (uintptr_t)SubtractStack),
|
||||
SubtractStack);
|
||||
#endif
|
||||
|
||||
if (Compatibility != Native)
|
||||
return;
|
||||
|
||||
#if defined(a64)
|
||||
this->Registers.rdi = (uintptr_t)argvLen; // argc
|
||||
this->Registers.rsi = (uintptr_t)(this->Registers.rsp + 8); // argv
|
||||
this->Registers.rcx = (uintptr_t)envpLen; // envc
|
||||
this->Registers.rdx = (uintptr_t)(this->Registers.rsp + 8 + (8 * argvLen) + 8); // envp
|
||||
#elif defined(a32)
|
||||
this->Registers.eax = (uintptr_t)argvLen; // argc
|
||||
this->Registers.ebx = (uintptr_t)(this->Registers.esp + 4); // argv
|
||||
this->Registers.ecx = (uintptr_t)envpLen; // envc
|
||||
this->Registers.edx = (uintptr_t)(this->Registers.esp + 4 + (4 * argvLen) + 4); // envp
|
||||
#elif defined(aa64)
|
||||
this->Registers.x0 = (uintptr_t)argvLen; // argc
|
||||
this->Registers.x1 = (uintptr_t)(this->Registers.sp + 8); // argv
|
||||
this->Registers.x2 = (uintptr_t)envpLen; // envc
|
||||
this->Registers.x3 = (uintptr_t)(this->Registers.sp + 8 + (8 * argvLen) + 8); // envp
|
||||
#endif
|
||||
}
|
||||
|
||||
void TCB::SetupUserStack_x86_32(const char **argv,
|
||||
const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv,
|
||||
TaskCompatibility Compatibility)
|
||||
{
|
||||
fixme("Not implemented");
|
||||
}
|
||||
|
||||
void TCB::SetupUserStack_aarch64(const char **argv,
|
||||
const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv,
|
||||
TaskCompatibility Compatibility)
|
||||
{
|
||||
fixme("Not implemented");
|
||||
}
|
||||
|
||||
void TCB::SetupThreadLocalStorage()
|
||||
{
|
||||
if (Parent->TLS.pBase == 0x0)
|
||||
return;
|
||||
this->TLS = Parent->TLS;
|
||||
|
||||
debug("msz: %ld fsz: %ld",
|
||||
this->TLS.Size, this->TLS.fSize);
|
||||
|
||||
// size_t pOffset = this->TLS.vBase - std::tolower(this->TLS.vBase);
|
||||
size_t tlsFullSize = sizeof(uintptr_t) + this->TLS.Size;
|
||||
/* 2 guard pages */
|
||||
size_t tlsPages = 1 + TO_PAGES(tlsFullSize) + 1;
|
||||
|
||||
void *opTLS = this->vma->RequestPages(tlsPages);
|
||||
void *pTLS = (void *)(PAGE_SIZE + uintptr_t(opTLS));
|
||||
this->TLS.pBase = uintptr_t(pTLS);
|
||||
|
||||
memcpy(pTLS, (void *)this->TLS.pBase,
|
||||
this->TLS.Size);
|
||||
|
||||
size_t restBytes = this->TLS.Size - this->TLS.fSize;
|
||||
if (restBytes)
|
||||
{
|
||||
memset((void *)(uintptr_t(pTLS) + this->TLS.Size),
|
||||
0, restBytes);
|
||||
}
|
||||
|
||||
Memory::Virtual vmm(this->Parent->PageTable);
|
||||
/* Map guard pages */
|
||||
vmm.Remap((void *)(uintptr_t(pTLS) - PAGE_SIZE), opTLS, Memory::P);
|
||||
vmm.Remap((void *)(uintptr_t(pTLS) + this->TLS.Size), opTLS, Memory::P);
|
||||
/* Map TLS */
|
||||
vmm.Map(pTLS, pTLS, this->TLS.Size, Memory::RW | Memory::US);
|
||||
|
||||
uintptr_t *pTLSPointer = (uintptr_t *)this->TLS.pBase + this->TLS.Size;
|
||||
*pTLSPointer = this->TLS.pBase + this->TLS.Size;
|
||||
|
||||
this->GSBase = r_cst(uintptr_t, pTLSPointer);
|
||||
this->FSBase = r_cst(uintptr_t, pTLSPointer);
|
||||
}
|
||||
|
||||
TCB::TCB(Task *ctx, PCB *Parent, IP EntryPoint,
|
||||
const char **argv, const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv,
|
||||
TaskArchitecture Architecture,
|
||||
TaskCompatibility Compatibility,
|
||||
bool ThreadNotReady)
|
||||
: Signals(Parent->Signals)
|
||||
{
|
||||
debug("+ %#lx", this);
|
||||
|
||||
assert(ctx != nullptr);
|
||||
assert(Architecture >= _ArchitectureMin);
|
||||
assert(Architecture <= _ArchitectureMax);
|
||||
assert(Compatibility >= _CompatibilityMin);
|
||||
assert(Compatibility <= _CompatibilityMax);
|
||||
|
||||
if (Parent == nullptr)
|
||||
{
|
||||
this->Parent = ctx->GetCurrentProcess();
|
||||
assert(this->Parent != nullptr);
|
||||
}
|
||||
else
|
||||
this->Parent = Parent;
|
||||
|
||||
this->ctx = ctx;
|
||||
this->ID = (TID)this->Parent->ID + (TID)this->Parent->Threads.size();
|
||||
|
||||
if (Compatibility == TaskCompatibility::Linux)
|
||||
{
|
||||
if (Parent->Threads.size() == 0)
|
||||
this->Linux.tgid = Parent->ID;
|
||||
else
|
||||
this->Linux.tgid = Parent->Threads.front()->Linux.tgid;
|
||||
}
|
||||
|
||||
if (this->Name)
|
||||
delete[] this->Name;
|
||||
|
||||
this->Name = new char[strlen(this->Parent->Name) + 1];
|
||||
strcpy((char *)this->Name, this->Parent->Name);
|
||||
|
||||
this->EntryPoint = EntryPoint;
|
||||
this->ExitCode = KILL_CRASH;
|
||||
|
||||
if (ThreadNotReady)
|
||||
this->SetState(Waiting);
|
||||
else
|
||||
this->SetState(Ready);
|
||||
|
||||
this->vma = this->Parent->vma;
|
||||
|
||||
#if defined(a64)
|
||||
this->Registers.rip = EntryPoint;
|
||||
#elif defined(a32)
|
||||
this->Registers.eip = EntryPoint;
|
||||
#elif defined(aa64)
|
||||
this->Registers.pc = EntryPoint;
|
||||
#endif
|
||||
|
||||
switch (this->Parent->Security.ExecutionMode)
|
||||
{
|
||||
case TaskExecutionMode::System:
|
||||
fixme("System mode not supported.");
|
||||
[[fallthrough]];
|
||||
case TaskExecutionMode::Kernel:
|
||||
{
|
||||
this->Security.IsCritical = true;
|
||||
this->Stack = new Memory::StackGuard(false, this->vma);
|
||||
|
||||
#if defined(a64)
|
||||
this->ShadowGSBase =
|
||||
CPU::x64::rdmsr(CPU::x64::MSRID::MSR_SHADOW_GS_BASE);
|
||||
this->GSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_GS_BASE);
|
||||
this->FSBase = CPU::x64::rdmsr(CPU::x64::MSRID::MSR_FS_BASE);
|
||||
this->Registers.cs = GDT_KERNEL_CODE;
|
||||
this->Registers.ss = GDT_KERNEL_DATA;
|
||||
this->Registers.rflags.AlwaysOne = 1;
|
||||
this->Registers.rflags.IF = 1;
|
||||
this->Registers.rflags.ID = 1;
|
||||
this->Registers.rflags.AC = 0;
|
||||
this->Registers.rsp = ((uintptr_t)this->Stack->GetStackTop());
|
||||
POKE(uintptr_t, this->Registers.rsp) = (uintptr_t)ThreadDoExit;
|
||||
#elif defined(a32)
|
||||
this->Registers.cs = GDT_KERNEL_CODE;
|
||||
this->Registers.r3_ss = GDT_KERNEL_DATA;
|
||||
this->Registers.eflags.AlwaysOne = 1;
|
||||
this->Registers.eflags.IF = 1;
|
||||
this->Registers.eflags.ID = 1;
|
||||
this->Registers.eflags.AC = 0;
|
||||
this->Registers.esp = ((uintptr_t)this->Stack->GetStackTop());
|
||||
POKE(uintptr_t, this->Registers.esp) = (uintptr_t)ThreadDoExit;
|
||||
#elif defined(aa64)
|
||||
this->Registers.pc = EntryPoint;
|
||||
this->Registers.sp = ((uintptr_t)this->Stack->GetStackTop());
|
||||
POKE(uintptr_t, this->Registers.sp) = (uintptr_t)ThreadDoExit;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case TaskExecutionMode::User:
|
||||
{
|
||||
this->Stack = new Memory::StackGuard(true, this->vma);
|
||||
|
||||
Memory::VirtualAllocation::AllocatedPages gst = this->ctx->va.RequestPages(TO_PAGES(sizeof(gsTCB)));
|
||||
this->ctx->va.MapTo(gst, this->Parent->PageTable);
|
||||
gsTCB *gsT = (gsTCB *)gst.PhysicalAddress;
|
||||
#ifdef DEBUG
|
||||
gsT->__stub = 0xFFFFFFFFFFFFFFFF;
|
||||
#endif
|
||||
|
||||
gsT->ScPages = TO_PAGES(STACK_SIZE);
|
||||
|
||||
Memory::VirtualAllocation::AllocatedPages ssb = this->ctx->va.RequestPages(gsT->ScPages);
|
||||
this->ctx->va.MapTo(ssb, this->Parent->PageTable);
|
||||
gsT->SyscallStackBase = ssb.VirtualAddress;
|
||||
gsT->SyscallStack = (void *)((uintptr_t)gsT->SyscallStackBase + STACK_SIZE - 0x10);
|
||||
debug("New syscall stack created: %#lx (base: %#lx) with gs base at %#lx",
|
||||
gsT->SyscallStack, gsT->SyscallStackBase, gsT);
|
||||
|
||||
gsT->TempStack = 0x0;
|
||||
gsT->t = this;
|
||||
#if defined(a64)
|
||||
this->ShadowGSBase = (uintptr_t)gst.VirtualAddress;
|
||||
this->GSBase = 0;
|
||||
this->FSBase = 0;
|
||||
this->Registers.cs = GDT_USER_CODE;
|
||||
this->Registers.ss = GDT_USER_DATA;
|
||||
this->Registers.rflags.AlwaysOne = 1;
|
||||
this->Registers.rflags.IF = 1;
|
||||
this->Registers.rflags.ID = 1;
|
||||
this->Registers.rflags.AC = 0;
|
||||
/* We need to leave the libc's crt
|
||||
to make a syscall when the Thread
|
||||
is exited or we are going to get
|
||||
an exception. */
|
||||
|
||||
this->SetupUserStack_x86_64(argv, envp, auxv, Compatibility);
|
||||
#elif defined(a32)
|
||||
this->Registers.cs = GDT_USER_CODE;
|
||||
this->Registers.r3_ss = GDT_USER_DATA;
|
||||
this->Registers.eflags.AlwaysOne = 1;
|
||||
this->Registers.eflags.IF = 1;
|
||||
this->Registers.eflags.ID = 1;
|
||||
this->Registers.eflags.AC = 0;
|
||||
/* We need to leave the libc's crt
|
||||
to make a syscall when the Thread
|
||||
is exited or we are going to get
|
||||
an exception. */
|
||||
|
||||
this->SetupUserStack_x86_32(argv, envp, auxv);
|
||||
#elif defined(aa64)
|
||||
this->SetupUserStack_aarch64(argv, envp, auxv);
|
||||
#endif
|
||||
#ifdef DEBUG_TASKING
|
||||
DumpData(this->Name, this->Stack, STACK_SIZE);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
SetupThreadLocalStorage();
|
||||
|
||||
this->Info.Architecture = Architecture;
|
||||
this->Info.Compatibility = Compatibility;
|
||||
this->Security.ExecutionMode = this->Parent->Security.ExecutionMode;
|
||||
|
||||
switch (Compatibility)
|
||||
{
|
||||
case TaskCompatibility::Native:
|
||||
// this->Info.RootNode = fs->FileSystemRoots->GetChildren()[0];
|
||||
break;
|
||||
case TaskCompatibility::Linux:
|
||||
// this->Info.RootNode = fs->FileSystemRoots->GetChildren()[1];
|
||||
break;
|
||||
case TaskCompatibility::Windows:
|
||||
// this->Info.RootNode = fs->FileSystemRoots->GetChildren()[2];
|
||||
break;
|
||||
default:
|
||||
assert(!"Invalid compatibility mode");
|
||||
break;
|
||||
}
|
||||
/* FIXME */
|
||||
this->Info.RootNode = fs->GetRoot(0);
|
||||
|
||||
if (this->Parent->Threads.size() == 0)
|
||||
{
|
||||
this->Parent->Info.Architecture = this->Info.Architecture;
|
||||
this->Parent->Info.Compatibility = this->Info.Compatibility;
|
||||
this->Parent->Info.RootNode = this->Info.RootNode;
|
||||
}
|
||||
|
||||
// TODO: Is really a good idea to use the FPU in kernel mode?
|
||||
this->FPU.mxcsr = 0b0001111110000000;
|
||||
this->FPU.mxcsrmask = 0b1111111110111111;
|
||||
this->FPU.fcw = 0b0000001100111111;
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef a64
|
||||
debug("Thread EntryPoint: %#lx => RIP: %#lx",
|
||||
this->EntryPoint, this->Registers.rip);
|
||||
if (this->Parent->Security.ExecutionMode == TaskExecutionMode::User)
|
||||
debug("Thread stack region is %#lx-%#lx (U) and rsp is %#lx",
|
||||
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
|
||||
this->Registers.rsp);
|
||||
else
|
||||
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx",
|
||||
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
|
||||
this->Registers.rsp);
|
||||
#elif defined(a32)
|
||||
debug("Thread EntryPoint: %#lx => RIP: %#lx",
|
||||
this->EntryPoint, this->Registers.eip);
|
||||
if (Parent->Security.ExecutionMode == TaskExecutionMode::User)
|
||||
debug("Thread stack region is %#lx-%#lx (U) and rsp is %#lx",
|
||||
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
|
||||
this->Registers.esp);
|
||||
else
|
||||
debug("Thread stack region is %#lx-%#lx (K) and rsp is %#lx",
|
||||
this->Stack->GetStackBottom(), this->Stack->GetStackTop(),
|
||||
this->Registers.esp);
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
debug("Created %s thread \"%s\"(%d) in process \"%s\"(%d)",
|
||||
this->Security.ExecutionMode == TaskExecutionMode::User ? "user" : "kernel",
|
||||
this->Name, this->ID, this->Parent->Name,
|
||||
this->Parent->ID);
|
||||
#endif
|
||||
|
||||
this->AllocatedMemory += sizeof(TCB);
|
||||
this->AllocatedMemory += strlen(this->Parent->Name) + 1;
|
||||
this->AllocatedMemory += sizeof(Memory::StackGuard);
|
||||
|
||||
this->Info.SpawnTime = TimeManager->GetCounter();
|
||||
this->Parent->Threads.push_back(this);
|
||||
|
||||
if (this->Parent->Threads.size() == 1 &&
|
||||
this->Parent->State == Waiting &&
|
||||
ThreadNotReady == false)
|
||||
{
|
||||
this->Parent->SetState(Ready);
|
||||
debug("Setting process \"%s\"(%d) to ready",
|
||||
this->Parent->Name, this->Parent->ID);
|
||||
}
|
||||
}
|
||||
|
||||
TCB::~TCB()
|
||||
{
|
||||
debug("- %#lx", this);
|
||||
|
||||
/* Remove us from the process list so we
|
||||
don't get scheduled anymore */
|
||||
this->Parent->Threads.erase(std::find(this->Parent->Threads.begin(),
|
||||
this->Parent->Threads.end(),
|
||||
this));
|
||||
|
||||
/* Free CPU Stack */
|
||||
delete this->Stack;
|
||||
|
||||
/* Free Name */
|
||||
delete[] this->Name;
|
||||
|
||||
debug("Thread \"%s\"(%d) destroyed",
|
||||
this->Name, this->ID);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user