mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-07-10 23:09:18 +00:00
Restructured and rewritten entire codebase
This commit is contained in:
277
tasking/ipc.cpp
Normal file
277
tasking/ipc.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ipc.hpp>
|
||||
|
||||
#include <task.hpp>
|
||||
|
||||
#include "../kernel.h"
|
||||
#include "../ipc.h"
|
||||
|
||||
namespace InterProcessCommunication
|
||||
{
|
||||
void IPC::Fork(IPC *Parent)
|
||||
{
|
||||
std::vector<IPCHandle *> ParentHandles = Parent->GetHandles();
|
||||
|
||||
foreach (auto Hnd in ParentHandles)
|
||||
{
|
||||
debug("Forking IPC with ID %d", Hnd->ID);
|
||||
IPCHandle *NewHnd = (IPCHandle *)vma->RequestPages(TO_PAGES(sizeof(IPCHandle) + 1));
|
||||
memcpy(NewHnd, Hnd, sizeof(IPCHandle));
|
||||
NewHnd->Node = fs->Create(Hnd->Node->Name, vfs::NodeType::FILE, IPCNode);
|
||||
Handles.push_back(NewHnd);
|
||||
}
|
||||
}
|
||||
|
||||
IPCHandle *IPC::Create(IPCType Type, char UniqueToken[16])
|
||||
{
|
||||
UNUSED(Type);
|
||||
SmartLock(this->IPCLock);
|
||||
IPCHandle *Hnd = (IPCHandle *)vma->RequestPages(TO_PAGES(sizeof(IPCHandle) + 1));
|
||||
|
||||
Hnd->ID = NextID++;
|
||||
Hnd->Node = fs->Create(UniqueToken, vfs::NodeType::FILE, IPCNode);
|
||||
Hnd->Buffer = nullptr;
|
||||
Hnd->Length = 0;
|
||||
Hnd->Listening = false;
|
||||
|
||||
Handles.push_back(Hnd);
|
||||
debug("Created IPC with ID %d", Hnd->ID);
|
||||
return Hnd;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Destroy(IPCID ID)
|
||||
{
|
||||
SmartLock(this->IPCLock);
|
||||
forItr(itr, Handles)
|
||||
{
|
||||
if ((*itr)->ID == ID)
|
||||
{
|
||||
fs->Delete((*itr)->Node);
|
||||
vma->FreePages((*itr), TO_PAGES(sizeof(IPCHandle) + 1));
|
||||
Handles.erase(itr);
|
||||
debug("Destroyed IPC with ID %d", ID);
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Failed to destroy IPC with ID %d", ID);
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Allocate(IPCID ID, long Size)
|
||||
{
|
||||
SmartLock(this->IPCLock);
|
||||
if (Size < 0)
|
||||
return IPCError;
|
||||
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
if (Hnd->Buffer != nullptr || Hnd->Length != 0)
|
||||
return IPCAlreadyAllocated;
|
||||
|
||||
Hnd->Buffer = (uint8_t *)vma->RequestPages(TO_PAGES(Size + 1));
|
||||
Hnd->Length = Size;
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Deallocate(IPCID ID)
|
||||
{
|
||||
SmartLock(this->IPCLock);
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
if (Hnd->Buffer == nullptr || Hnd->Length == 0)
|
||||
return IPCNotAllocated;
|
||||
|
||||
vma->FreePages(Hnd->Buffer, TO_PAGES(Hnd->Length + 1));
|
||||
Hnd->Buffer = nullptr;
|
||||
Hnd->Length = 0;
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Read(IPCID ID, void *Buffer, long Size)
|
||||
{
|
||||
SmartLock(this->IPCLock);
|
||||
if (Size < 0)
|
||||
return IPCError;
|
||||
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
if (Hnd->Listening)
|
||||
{
|
||||
debug("IPC %d is listening", ID);
|
||||
return IPCNotListening;
|
||||
}
|
||||
if (Hnd->Length < Size)
|
||||
{
|
||||
debug("IPC %d is too small", ID);
|
||||
return IPCError;
|
||||
}
|
||||
debug("IPC %d reading %d bytes", ID, Size);
|
||||
memcpy(Buffer, Hnd->Buffer, Size);
|
||||
debug("IPC read %d bytes", Size);
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
debug("IPC %d not found", ID);
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Write(IPCID ID, void *Buffer, long Size)
|
||||
{
|
||||
SmartLock(this->IPCLock);
|
||||
if (Size < 0)
|
||||
{
|
||||
debug("IPC %d is too small", ID);
|
||||
return IPCError;
|
||||
}
|
||||
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
if (!Hnd->Listening)
|
||||
{
|
||||
debug("IPC %d is NOT listening", ID);
|
||||
return IPCNotListening;
|
||||
}
|
||||
if (Hnd->Length < Size)
|
||||
{
|
||||
debug("IPC %d is too small", ID);
|
||||
return IPCError;
|
||||
}
|
||||
debug("IPC %d writing %d bytes", ID, Size);
|
||||
memcpy(Hnd->Buffer, Buffer, Size);
|
||||
Hnd->Listening = false;
|
||||
debug("IPC %d wrote %d bytes and now is %s", ID, Size, Hnd->Listening ? "listening" : "ready");
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
debug("IPC %d not found", ID);
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Listen(IPCID ID, bool Listen)
|
||||
{
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
Hnd->Listening = Listen;
|
||||
debug("IPC %d is now set to %s", ID, Listen ? "listening" : "ready");
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
debug("IPC %d not found", ID);
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCErrorCode IPC::Wait(IPCID ID)
|
||||
{
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (Hnd->ID == ID)
|
||||
{
|
||||
if (!CPU::Interrupts())
|
||||
warn("Interrupts are disabled. This may cause a kernel hang.");
|
||||
debug("Waiting for IPC %d (now %s)", ID, Hnd->Listening ? "listening" : "ready");
|
||||
while (Hnd->Listening)
|
||||
TaskManager->Yield();
|
||||
debug("IPC %d is ready", ID);
|
||||
return IPCSuccess;
|
||||
}
|
||||
}
|
||||
debug("IPC %d not found", ID);
|
||||
return IPCIDNotFound;
|
||||
}
|
||||
|
||||
IPCHandle *IPC::SearchByToken(char UniqueToken[16])
|
||||
{
|
||||
foreach (auto Hnd in Handles)
|
||||
{
|
||||
if (strcmp(Hnd->Node->Name, UniqueToken) == 0)
|
||||
{
|
||||
debug("Found IPC with token %s", UniqueToken);
|
||||
return Hnd;
|
||||
}
|
||||
}
|
||||
debug("Failed to find IPC with token %s", UniqueToken);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int IPC::HandleSyscall(long Command, long Type, int ID, int Flags, void *Buffer, size_t Size)
|
||||
{
|
||||
switch (Command)
|
||||
{
|
||||
case IPC_CREATE:
|
||||
{
|
||||
char UniqueToken[16];
|
||||
if (Buffer != nullptr)
|
||||
strcpy(UniqueToken, (char *)Buffer);
|
||||
else
|
||||
snprintf(UniqueToken, 16, "IPC_%d", ID);
|
||||
IPCHandle *Hnd = this->Create((IPCType)Type, UniqueToken);
|
||||
this->Allocate(Hnd->ID, Size ? Size : PAGE_SIZE);
|
||||
return Hnd->ID;
|
||||
}
|
||||
case IPC_READ:
|
||||
return this->Read(ID, Buffer, Size);
|
||||
case IPC_WRITE:
|
||||
return TaskManager->GetProcessByID(Flags)->IPC->Write(ID, Buffer, Size);
|
||||
case IPC_DELETE:
|
||||
{
|
||||
this->Deallocate(ID);
|
||||
return this->Destroy(ID);
|
||||
}
|
||||
case IPC_WAIT:
|
||||
return this->Wait(ID);
|
||||
case IPC_LISTEN:
|
||||
return this->Listen(ID, Flags);
|
||||
default:
|
||||
return IPCInvalidCommand;
|
||||
}
|
||||
return IPCError;
|
||||
}
|
||||
|
||||
IPC::IPC(void *Process)
|
||||
{
|
||||
Tasking::PCB *pcb = (Tasking::PCB *)Process;
|
||||
this->Process = Process;
|
||||
this->vma = new Memory::VirtualMemoryArea(pcb->PageTable);
|
||||
IPCNode = fs->Create("ipc", vfs::NodeType::DIRECTORY,
|
||||
pcb->ProcessDirectory);
|
||||
}
|
||||
|
||||
IPC::~IPC()
|
||||
{
|
||||
fs->Delete(IPCNode, true);
|
||||
delete this->vma, this->vma = nullptr;
|
||||
}
|
||||
}
|
267
tasking/process.cpp
Normal file
267
tasking/process.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
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 "../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 InterProcessCommunication;
|
||||
using namespace vfs;
|
||||
using vfs::NodeType;
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
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(vfs::Node *node)
|
||||
{
|
||||
trace("Setting working directory of process %s to %#lx (%s)",
|
||||
this->Name, node, node->Name);
|
||||
CurrentWorkingDirectory = node;
|
||||
}
|
||||
|
||||
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, void *Image,
|
||||
bool DoNotCreatePageTable,
|
||||
uint16_t UserID, uint16_t GroupID)
|
||||
{
|
||||
debug("+ %#lx", this);
|
||||
|
||||
assert(ctx != nullptr);
|
||||
assert(Name != nullptr);
|
||||
assert(strlen(Name) > 0);
|
||||
assert(ExecutionMode >= _ExecuteModeMin);
|
||||
assert(ExecutionMode <= _ExecuteModeMax);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char ProcFSName[12];
|
||||
sprintf(ProcFSName, "%d", this->ID);
|
||||
this->ProcessDirectory = fs->Create(ProcFSName, DIRECTORY, ProcFS);
|
||||
this->FileDescriptors = new FileDescriptorTable(this);
|
||||
|
||||
/* If create page table */
|
||||
if (DoNotCreatePageTable == false)
|
||||
{
|
||||
OwnPageTable = true;
|
||||
|
||||
size_t PTPgs = TO_PAGES(sizeof(Memory::PageTable) + 1);
|
||||
this->PageTable = (Memory::PageTable *)KernelAllocator.RequestPages(PTPgs);
|
||||
memcpy(this->PageTable, KernelPageTable, sizeof(Memory::PageTable));
|
||||
|
||||
debug("Process %s(%d) has page table at %#lx",
|
||||
this->Name, this->ID, this->PageTable);
|
||||
}
|
||||
|
||||
this->vma = new Memory::VirtualMemoryArea(this->PageTable);
|
||||
this->ProgramBreak = new Memory::ProgramBreak(this->PageTable, this->vma);
|
||||
this->IPC = new class IPC((void *)this);
|
||||
|
||||
if (Image)
|
||||
{
|
||||
this->ELFSymbolTable = new SymbolResolver::Symbols((uintptr_t)Image);
|
||||
this->AllocatedMemory += sizeof(SymbolResolver::Symbols);
|
||||
}
|
||||
|
||||
if (Parent)
|
||||
Parent->Children.push_back(this);
|
||||
|
||||
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(class IPC);
|
||||
|
||||
this->Info.SpawnTime = TimeManager->GetCounter();
|
||||
ctx->ProcessList.push_back(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->ProcessList.erase(std::find(ctx->ProcessList.begin(),
|
||||
ctx->ProcessList.end(),
|
||||
this));
|
||||
|
||||
debug("Freeing symbol table strings");
|
||||
if (this->ELFSymbolTable)
|
||||
delete this->ELFSymbolTable;
|
||||
|
||||
debug("Freeing IPC");
|
||||
delete this->IPC;
|
||||
|
||||
debug("Freeing allocated memory");
|
||||
delete this->ProgramBreak;
|
||||
delete this->vma;
|
||||
|
||||
debug("Closing file descriptors");
|
||||
delete this->FileDescriptors;
|
||||
|
||||
/* 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)
|
||||
delete pcb;
|
||||
|
||||
/* Exit all threads */
|
||||
foreach (auto tcb in this->Threads)
|
||||
delete tcb;
|
||||
|
||||
debug("Removing /proc/%d", this->ID);
|
||||
fs->Delete(this->ProcessDirectory, true);
|
||||
|
||||
/* Free Name */
|
||||
delete[] this->Name;
|
||||
|
||||
debug("Removing from parent process");
|
||||
if (this->Parent)
|
||||
{
|
||||
std::vector<Tasking::PCB *> &pChild = this->Parent->Children;
|
||||
|
||||
pChild.erase(std::find(pChild.begin(),
|
||||
pChild.end(),
|
||||
this));
|
||||
}
|
||||
}
|
||||
}
|
769
tasking/scheduler.cpp
Normal file
769
tasking/scheduler.cpp
Normal file
@ -0,0 +1,769 @@
|
||||
/*
|
||||
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 "../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
|
||||
|
||||
/* FIXME: On screen task manager is corrupting the stack... */
|
||||
// #define ON_SCREEN_SCHEDULER_TASK_MANAGER 1
|
||||
|
||||
// #define DEBUG_SCHEDULER 1
|
||||
// #define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1
|
||||
// #define DEBUG_GET_NEXT_AVAILABLE_THREAD 1
|
||||
// #define DEBUG_FIND_NEW_PROCESS 1
|
||||
// #define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1
|
||||
// #define DEBUG_WAKE_UP_THREADS 1
|
||||
|
||||
/* Global */
|
||||
#ifdef DEBUG_SCHEDULER
|
||||
|
||||
#define DEBUG_GET_NEXT_AVAILABLE_PROCESS 1
|
||||
#define DEBUG_GET_NEXT_AVAILABLE_THREAD 1
|
||||
#define DEBUG_FIND_NEW_PROCESS 1
|
||||
#define DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD 1
|
||||
#define DEBUG_WAKE_UP_THREADS 1
|
||||
|
||||
#define schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
/* GetNextAvailableThread */
|
||||
#ifdef DEBUG_GET_NEXT_AVAILABLE_PROCESS
|
||||
#define gnap_schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define gnap_schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
/* GetNextAvailableProcess */
|
||||
#ifdef DEBUG_GET_NEXT_AVAILABLE_THREAD
|
||||
#define gnat_schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define gnat_schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
/* FindNewProcess */
|
||||
#ifdef DEBUG_FIND_NEW_PROCESS
|
||||
#define fnp_schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define fnp_schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
/* SchedulerSearchProcessThread */
|
||||
#ifdef DEBUG_SCHEDULER_SEARCH_PROCESS_THREAD
|
||||
#define sspt_schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define sspt_schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
/* WakeUpThreads */
|
||||
#ifdef DEBUG_WAKE_UP_THREADS
|
||||
#define wut_schedbg(m, ...) \
|
||||
debug(m, ##__VA_ARGS__); \
|
||||
__sync
|
||||
#else
|
||||
#define wut_schedbg(m, ...)
|
||||
#endif
|
||||
|
||||
extern "C" SafeFunction NIF void TaskingScheduler_OneShot(int TimeSlice)
|
||||
{
|
||||
if (TimeSlice == 0)
|
||||
TimeSlice = Tasking::TaskPriority::Normal;
|
||||
#if defined(a86)
|
||||
((APIC::Timer *)Interrupts::apicTimer[GetCurrentCPU()->ID])->OneShot(CPU::x86::IRQ16, TimeSlice);
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
#if defined(a86)
|
||||
SafeFunction NIF bool Task::FindNewProcess(void *CPUDataPointer)
|
||||
{
|
||||
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
||||
fnp_schedbg("%d processes", ProcessList.size());
|
||||
#ifdef DEBUG_FIND_NEW_PROCESS
|
||||
foreach (auto process in ProcessList)
|
||||
fnp_schedbg("Process %d %s", process->ID,
|
||||
process->Name);
|
||||
#endif
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (unlikely(InvalidPCB(process)))
|
||||
continue;
|
||||
|
||||
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 (unlikely(InvalidTCB(thread)))
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SafeFunction NIF bool Task::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];
|
||||
|
||||
if (unlikely(InvalidTCB(nextThread)))
|
||||
{
|
||||
if (TempIndex > ThreadsSize)
|
||||
break;
|
||||
TempIndex++;
|
||||
|
||||
gnat_schedbg("Thread %#lx is invalid", nextThread);
|
||||
goto RetryAnotherThread;
|
||||
}
|
||||
|
||||
gnat_schedbg("\"%s\"(%d) and next thread is \"%s\"(%d)",
|
||||
CurrentCPU->CurrentProcess->Threads[i]->Name,
|
||||
CurrentCPU->CurrentProcess->Threads[i]->ID,
|
||||
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;
|
||||
}
|
||||
|
||||
SafeFunction NIF bool Task::GetNextAvailableProcess(void *CPUDataPointer)
|
||||
{
|
||||
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
||||
|
||||
bool Skip = true;
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (process == CurrentCPU->CurrentProcess.load())
|
||||
{
|
||||
Skip = false;
|
||||
gnap_schedbg("Found current process %#lx", process);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip)
|
||||
{
|
||||
gnap_schedbg("Skipping process %#lx", process);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(InvalidPCB(process)))
|
||||
{
|
||||
gnap_schedbg("Invalid 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 (unlikely(InvalidTCB(thread)))
|
||||
{
|
||||
gnap_schedbg("Invalid thread %#lx", thread);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SafeFunction NIF bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
|
||||
{
|
||||
CPUData *CurrentCPU = (CPUData *)CPUDataPointer;
|
||||
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (unlikely(InvalidPCB(process)))
|
||||
{
|
||||
sspt_schedbg("Invalid process %#lx", process);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (process->State.load() != TaskState::Ready)
|
||||
{
|
||||
sspt_schedbg("Process %d is not ready", process->ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (unlikely(InvalidTCB(thread)))
|
||||
{
|
||||
sspt_schedbg("Invalid thread %#lx", thread);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SafeFunction NIF void Task::UpdateProcessState()
|
||||
{
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (unlikely(InvalidPCB(process)))
|
||||
continue;
|
||||
|
||||
if (process->State.load() == TaskState::Terminated)
|
||||
continue;
|
||||
|
||||
if (process->Threads.size() == 1)
|
||||
{
|
||||
process->State.exchange(process->Threads[0]->State.load());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool AllThreadsSleeping = true;
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
SafeFunction NIF void Task::WakeUpThreads()
|
||||
{
|
||||
foreach (auto process in ProcessList)
|
||||
{
|
||||
if (unlikely(InvalidPCB(process)))
|
||||
continue;
|
||||
|
||||
Tasking::TaskState pState = process->State.load();
|
||||
if (pState != TaskState::Ready &&
|
||||
pState != TaskState::Sleeping &&
|
||||
pState != TaskState::Blocked)
|
||||
continue;
|
||||
|
||||
foreach (auto thread in process->Threads)
|
||||
{
|
||||
if (unlikely(InvalidTCB(thread)))
|
||||
continue;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
|
||||
int SuccessSource = 0;
|
||||
int sanity;
|
||||
|
||||
const char *Statuses[] = {
|
||||
"FF0000", /* Unknown */
|
||||
"AAFF00", /* Ready */
|
||||
"00AA00", /* Running */
|
||||
"FFAA00", /* Sleeping */
|
||||
"FFAA00", /* Blocked */
|
||||
"FFAA00", /* Stopped */
|
||||
"FFAA00", /* Waiting */
|
||||
|
||||
"FF0088", /* Zombie */
|
||||
"FF0000", /* Terminated */
|
||||
};
|
||||
|
||||
const char *StatusesSign[] = {
|
||||
"Unknown",
|
||||
"Ready",
|
||||
"Run",
|
||||
"Sleep",
|
||||
"Block",
|
||||
"Wait",
|
||||
|
||||
"Zombie",
|
||||
"Terminated",
|
||||
};
|
||||
|
||||
const char *SuccessSourceStrings[] = {
|
||||
"Unknown",
|
||||
"GetNextAvailableThread",
|
||||
"GetNextAvailableProcess",
|
||||
"SchedulerSearchProcessThread",
|
||||
};
|
||||
|
||||
SafeFunction NIF void OnScreenTaskManagerUpdate()
|
||||
{
|
||||
TimeManager->Sleep(10, Time::Units::Milliseconds);
|
||||
Video::ScreenBuffer *sb = Display->GetBuffer(0);
|
||||
for (short i = 0; i < 340; i++)
|
||||
{
|
||||
for (short j = 0; j < 600; j++)
|
||||
{
|
||||
uint32_t *Pixel = (uint32_t *)((uintptr_t)sb->Buffer + (j * sb->Width + i) * (bInfo.Framebuffer[0].BitsPerPixel / 8));
|
||||
*Pixel = 0x222222;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tmpX, tmpY;
|
||||
Display->GetBufferCursor(0, &tmpX, &tmpY);
|
||||
Display->SetBufferCursor(0, 0, 0);
|
||||
printf("\eF02C21Task Manager\n");
|
||||
foreach (auto Proc in TaskManager->GetProcessList())
|
||||
{
|
||||
int State = Proc->State;
|
||||
printf("\e%s-> \eAABBCC%s \e00AAAA%s\n",
|
||||
Statuses[State], Proc->Name, StatusesSign[State]);
|
||||
|
||||
foreach (auto Thd in Proc->Threads)
|
||||
{
|
||||
State = Thd->State;
|
||||
printf(" \e%s-> \eAABBCC%s \e00AAAA%s\n\eAABBCC",
|
||||
Statuses[State], Thd->Name, StatusesSign[State]);
|
||||
}
|
||||
}
|
||||
register uintptr_t CurrentStackAddress asm("rsp");
|
||||
printf("Sanity: %d, Stack: %#lx\nSched. Source: %s", sanity++, CurrentStackAddress, SuccessSourceStrings[SuccessSource]);
|
||||
if (sanity > 1000)
|
||||
sanity = 0;
|
||||
Display->SetBufferCursor(0, tmpX, tmpY);
|
||||
Display->SetBuffer(0);
|
||||
TimeManager->Sleep(10, Time::Units::Milliseconds);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef a64
|
||||
SafeFunction NIF void Task::Schedule(CPU::x64::TrapFrame *Frame)
|
||||
#else
|
||||
SafeFunction NIF void Task::Schedule(CPU::x32::TrapFrame *Frame)
|
||||
#endif
|
||||
{
|
||||
if (unlikely(StopScheduler))
|
||||
{
|
||||
warn("Scheduler stopped.");
|
||||
return;
|
||||
}
|
||||
bool ProcessNotChanged = false;
|
||||
/* Restore kernel page table for safety reasons. */
|
||||
#ifdef a64
|
||||
CPU::x64::writecr3({.raw = (uint64_t)KernelPageTable});
|
||||
#else
|
||||
CPU::x32::writecr3({.raw = (uint32_t)KernelPageTable});
|
||||
#endif
|
||||
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);
|
||||
|
||||
#ifdef DEBUG_SCHEDULER
|
||||
{
|
||||
schedbg("================================================================");
|
||||
schedbg("State: 0-ukn | 1-rdy | 2-run | 3-wait | 4-term");
|
||||
schedbg("Technical Informations on regs %#lx", Frame->InterruptNumber);
|
||||
size_t ds;
|
||||
asmv("mov %%ds, %0"
|
||||
: "=r"(ds));
|
||||
schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx",
|
||||
CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE),
|
||||
Frame->ss, Frame->cs, ds);
|
||||
schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx",
|
||||
Frame->r8, Frame->r9, Frame->r10, Frame->r11);
|
||||
schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx",
|
||||
Frame->r12, Frame->r13, Frame->r14, Frame->r15);
|
||||
schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx",
|
||||
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
|
||||
schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx",
|
||||
Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp);
|
||||
schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx",
|
||||
Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode);
|
||||
schedbg("================================================================");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(InvalidPCB(CurrentCPU->CurrentProcess.load()) ||
|
||||
InvalidTCB(CurrentCPU->CurrentThread.load())))
|
||||
{
|
||||
schedbg("Invalid process or thread. Finding a new one.");
|
||||
ProcessNotChanged = true;
|
||||
if (this->FindNewProcess(CurrentCPU))
|
||||
goto Success;
|
||||
else
|
||||
goto Idle;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentCPU->CurrentThread->Registers = *Frame;
|
||||
CPU::x64::fxsave(&CurrentCPU->CurrentThread->FPU);
|
||||
#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->UpdateProcessState();
|
||||
schedbg("Passed UpdateProcessState");
|
||||
|
||||
this->WakeUpThreads();
|
||||
schedbg("Passed WakeUpThreads");
|
||||
|
||||
if (this->SchedulerUpdateTrapFrame)
|
||||
{
|
||||
this->SchedulerUpdateTrapFrame = false;
|
||||
goto Success;
|
||||
}
|
||||
|
||||
if (this->GetNextAvailableThread(CurrentCPU))
|
||||
{
|
||||
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
|
||||
SuccessSource = 1;
|
||||
#endif
|
||||
ProcessNotChanged = true;
|
||||
goto Success;
|
||||
}
|
||||
schedbg("Passed GetNextAvailableThread");
|
||||
|
||||
if (this->GetNextAvailableProcess(CurrentCPU))
|
||||
{
|
||||
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
|
||||
SuccessSource = 2;
|
||||
#endif
|
||||
goto Success;
|
||||
}
|
||||
schedbg("Passed GetNextAvailableProcess");
|
||||
|
||||
if (SchedulerSearchProcessThread(CurrentCPU))
|
||||
{
|
||||
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
|
||||
SuccessSource = 3;
|
||||
#endif
|
||||
schedbg("Passed SchedulerSearchProcessThread");
|
||||
goto Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
schedbg("SchedulerSearchProcessThread failed. Going idle.");
|
||||
goto Idle;
|
||||
}
|
||||
}
|
||||
|
||||
warn("Unwanted reach!");
|
||||
TaskingScheduler_OneShot(100);
|
||||
goto End;
|
||||
|
||||
Idle:
|
||||
ProcessNotChanged = true;
|
||||
CurrentCPU->CurrentProcess = IdleProcess;
|
||||
CurrentCPU->CurrentThread = IdleThread;
|
||||
|
||||
Success:
|
||||
schedbg("Process \"%s\"(%d) Thread \"%s\"(%d) is now running on CPU %d",
|
||||
CurrentCPU->CurrentProcess->Name, CurrentCPU->CurrentProcess->ID,
|
||||
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID, CurrentCPU->ID);
|
||||
|
||||
if (!ProcessNotChanged)
|
||||
UpdateUsage(&CurrentCPU->CurrentProcess->Info,
|
||||
CurrentCPU->CurrentProcess->Security.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);
|
||||
|
||||
*Frame = CurrentCPU->CurrentThread->Registers;
|
||||
|
||||
for (size_t i = 0; i < (sizeof(CurrentCPU->CurrentThread->IPHistory) / sizeof(CurrentCPU->CurrentThread->IPHistory[0])) - 1; i++)
|
||||
CurrentCPU->CurrentThread->IPHistory[i + 1] = CurrentCPU->CurrentThread->IPHistory[i];
|
||||
|
||||
#ifdef a64
|
||||
CurrentCPU->CurrentThread->IPHistory[0] = Frame->rip;
|
||||
|
||||
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
|
||||
CurrentCPU->CurrentThread->IPHistory[0] = Frame->eip;
|
||||
|
||||
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
|
||||
|
||||
#ifdef ON_SCREEN_SCHEDULER_TASK_MANAGER
|
||||
OnScreenTaskManagerUpdate();
|
||||
#endif
|
||||
|
||||
switch (CurrentCPU->CurrentProcess->Security.ExecutionMode)
|
||||
{
|
||||
case TaskExecutionMode::System:
|
||||
case TaskExecutionMode::Kernel:
|
||||
// wrmsr(MSR_SHADOW_GS_BASE, (uint64_t)CurrentCPU->CurrentThread);
|
||||
break;
|
||||
case TaskExecutionMode::User:
|
||||
// wrmsr(MSR_SHADOW_GS_BASE, CurrentCPU->CurrentThread->gs);
|
||||
break;
|
||||
default:
|
||||
error("Unknown trust level %d.",
|
||||
CurrentCPU->CurrentProcess->Security.ExecutionMode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ProcessNotChanged)
|
||||
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
|
||||
(&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter();
|
||||
TaskingScheduler_OneShot(CurrentCPU->CurrentThread->Info.Priority);
|
||||
|
||||
if (CurrentCPU->CurrentThread->Security.IsDebugEnabled && CurrentCPU->CurrentThread->Security.IsKernelDebugEnabled)
|
||||
{
|
||||
#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
|
||||
}
|
||||
|
||||
schedbg("================================================================");
|
||||
schedbg("Technical Informations on Thread %s[%ld]:",
|
||||
CurrentCPU->CurrentThread->Name, CurrentCPU->CurrentThread->ID);
|
||||
uintptr_t ds;
|
||||
asmv("mov %%ds, %0"
|
||||
: "=r"(ds));
|
||||
schedbg("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx",
|
||||
CPU::x64::rdmsr(CPU::x64::MSR_FS_BASE), CPU::x64::rdmsr(CPU::x64::MSR_GS_BASE),
|
||||
Frame->ss, Frame->cs, ds);
|
||||
schedbg("R8=%#lx R9=%#lx R10=%#lx R11=%#lx",
|
||||
Frame->r8, Frame->r9, Frame->r10, Frame->r11);
|
||||
schedbg("R12=%#lx R13=%#lx R14=%#lx R15=%#lx",
|
||||
Frame->r12, Frame->r13, Frame->r14, Frame->r15);
|
||||
schedbg("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx",
|
||||
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
|
||||
schedbg("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx",
|
||||
Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp);
|
||||
schedbg("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx",
|
||||
Frame->rip, Frame->rflags, Frame->InterruptNumber, Frame->ErrorCode);
|
||||
schedbg("================================================================");
|
||||
|
||||
End:
|
||||
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
|
||||
#ifdef a64
|
||||
CPU::x64::writecr3({.raw = (uint64_t)CurrentCPU->CurrentProcess->PageTable});
|
||||
#else
|
||||
CPU::x32::writecr3({.raw = (uint64_t)CurrentCPU->CurrentProcess->PageTable});
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef a64
|
||||
SafeFunction NIF void Task::OnInterruptReceived(CPU::x64::TrapFrame *Frame)
|
||||
#else
|
||||
SafeFunction NIF void Task::OnInterruptReceived(CPU::x32::TrapFrame *Frame)
|
||||
#endif
|
||||
{
|
||||
SmartCriticalSection(SchedulerLock);
|
||||
this->Schedule(Frame);
|
||||
}
|
||||
#elif defined(aa64)
|
||||
SafeFunction bool Task::FindNewProcess(void *CPUDataPointer)
|
||||
{
|
||||
fixme("unimplemented");
|
||||
}
|
||||
|
||||
SafeFunction bool Task::GetNextAvailableThread(void *CPUDataPointer)
|
||||
{
|
||||
fixme("unimplemented");
|
||||
}
|
||||
|
||||
SafeFunction bool Task::GetNextAvailableProcess(void *CPUDataPointer)
|
||||
{
|
||||
fixme("unimplemented");
|
||||
}
|
||||
|
||||
SafeFunction bool Task::SchedulerSearchProcessThread(void *CPUDataPointer)
|
||||
{
|
||||
fixme("unimplemented");
|
||||
}
|
||||
|
||||
SafeFunction void Task::Schedule(CPU::aarch64::TrapFrame *Frame)
|
||||
{
|
||||
fixme("unimplemented");
|
||||
}
|
||||
|
||||
SafeFunction void Task::OnInterruptReceived(CPU::aarch64::TrapFrame *Frame) { this->Schedule(Frame); }
|
||||
#endif
|
||||
}
|
464
tasking/task.cpp
Normal file
464
tasking/task.cpp
Normal file
@ -0,0 +1,464 @@
|
||||
/*
|
||||
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 "../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
|
||||
{
|
||||
#if defined(a86)
|
||||
__naked __used __no_stack_protector void IdleProcessLoop()
|
||||
{
|
||||
asmv("IdleLoop:");
|
||||
asmv("hlt");
|
||||
asmv("jmp IdleLoop");
|
||||
#elif defined(aa64)
|
||||
__used __no_stack_protector void IdleProcessLoop()
|
||||
{
|
||||
asmv("IdleLoop:");
|
||||
asmv("wfe");
|
||||
asmv("b IdleLoop");
|
||||
#endif
|
||||
}
|
||||
|
||||
SafeFunction bool Task::InvalidPCB(PCB *pcb)
|
||||
{
|
||||
if (!pcb)
|
||||
return true;
|
||||
|
||||
/* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
|
||||
if (pcb >= (PCB *)(UINTPTR_MAX - 0x1FFE))
|
||||
return true;
|
||||
|
||||
/* In this section of the memory is reserved by the kernel. */
|
||||
if (pcb < (PCB *)(0xFFFFF))
|
||||
return true;
|
||||
|
||||
/* Check if it's mapped. */
|
||||
if (!Memory::Virtual().Check((void *)pcb))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SafeFunction bool Task::InvalidTCB(TCB *tcb)
|
||||
{
|
||||
if (!tcb)
|
||||
return true;
|
||||
|
||||
/* Uninitialized pointers may have uintptr_t max value instead of nullptr. */
|
||||
if (tcb >= (TCB *)(UINTPTR_MAX - 0x1FFE))
|
||||
return true;
|
||||
|
||||
/* In this section of the memory is reserved by the kernel. */
|
||||
if (tcb < (TCB *)(0xFFFFF))
|
||||
return true;
|
||||
|
||||
/* Check if it's mapped. */
|
||||
if (!Memory::Virtual().Check((void *)tcb))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SafeFunction bool Task::RemoveThread(TCB *Thread)
|
||||
{
|
||||
if (Thread->KeepInMemory.load() == true)
|
||||
return false;
|
||||
if (Thread->KeepTime > TimeManager->GetCounter())
|
||||
return false;
|
||||
|
||||
debug("Thread \"%s\"(%d) removed from process \"%s\"(%d)",
|
||||
Thread->Name, Thread->ID, Thread->Parent->Name,
|
||||
Thread->Parent->ID);
|
||||
|
||||
delete Thread;
|
||||
return true;
|
||||
}
|
||||
|
||||
SafeFunction bool Task::RemoveProcess(PCB *Process)
|
||||
{
|
||||
if (unlikely(InvalidPCB(Process)))
|
||||
return false;
|
||||
|
||||
if (Process->State == Terminated)
|
||||
{
|
||||
if (Process->KeepInMemory.load() == true)
|
||||
return false;
|
||||
if (Process->KeepTime > TimeManager->GetCounter())
|
||||
return false;
|
||||
|
||||
delete Process;
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (TCB *Thread in Process->Threads)
|
||||
{
|
||||
if (Thread->State == Terminated)
|
||||
RemoveThread(Thread);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SafeFunction void Task::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core)
|
||||
{
|
||||
UNUSED(Core);
|
||||
uint64_t CurrentTime = TimeManager->GetCounter();
|
||||
uint64_t TimePassed = CurrentTime - Info->LastUpdateTime;
|
||||
// Info->LastUpdateTime = CurrentTime;
|
||||
|
||||
if (Mode == TaskExecutionMode::User)
|
||||
Info->UserTime += TimePassed;
|
||||
else
|
||||
Info->KernelTime += TimePassed;
|
||||
}
|
||||
|
||||
PCB *Task::GetCurrentProcess()
|
||||
{
|
||||
return GetCurrentCPU()->CurrentProcess.load();
|
||||
}
|
||||
|
||||
TCB *Task::GetCurrentThread()
|
||||
{
|
||||
return GetCurrentCPU()->CurrentThread.load();
|
||||
}
|
||||
|
||||
PCB *Task::GetProcessByID(TID ID)
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
foreach (auto p in ProcessList)
|
||||
{
|
||||
if (p->ID == ID)
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TCB *Task::GetThreadByID(TID ID)
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
foreach (auto p in ProcessList)
|
||||
{
|
||||
foreach (auto t in p->Threads)
|
||||
{
|
||||
if (t->ID == ID)
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Task::WaitForProcess(PCB *pcb)
|
||||
{
|
||||
if (unlikely(InvalidPCB(pcb)))
|
||||
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)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::WaitForThread(TCB *tcb)
|
||||
{
|
||||
if (unlikely(InvalidTCB(tcb)))
|
||||
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)
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
void Task::WaitForProcessStatus(PCB *pcb, TaskState status)
|
||||
{
|
||||
if (unlikely(InvalidPCB(pcb)))
|
||||
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 (unlikely(InvalidTCB(tcb)))
|
||||
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 = this->GetCurrentProcess();
|
||||
|
||||
thread->State = TaskState::Sleeping;
|
||||
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
if (process->Threads.size() == 1)
|
||||
process->State = 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()
|
||||
{
|
||||
fixme("SignalShutdown()");
|
||||
// TODO: Implement this
|
||||
// This should hang until all processes are terminated
|
||||
}
|
||||
|
||||
void Task::CleanupProcessesThread()
|
||||
{
|
||||
thisThread->Rename("Tasking Cleanup");
|
||||
while (true)
|
||||
{
|
||||
this->Sleep(2000);
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
foreach (auto Process in ProcessList)
|
||||
{
|
||||
if (unlikely(InvalidPCB(Process)))
|
||||
continue;
|
||||
|
||||
RemoveProcess(Process);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__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,
|
||||
void *Image,
|
||||
bool DoNotCreatePageTable,
|
||||
uint16_t UserID,
|
||||
uint16_t GroupID)
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
return new PCB(this, Parent, Name, ExecutionMode,
|
||||
Image, DoNotCreatePageTable,
|
||||
UserID, GroupID);
|
||||
}
|
||||
|
||||
Task::Task(const IP EntryPoint) : Interrupts::Handler(16) /* IRQ16 */
|
||||
{
|
||||
#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));
|
||||
|
||||
#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", TaskExecutionMode::Kernel);
|
||||
kproc->ELFSymbolTable = KernelSymbolTable;
|
||||
TCB *kthrd = CreateThread(kproc, EntryPoint,
|
||||
nullptr, nullptr,
|
||||
std::vector<AuxiliaryVector>(), Arch);
|
||||
kthrd->Rename("Main Thread");
|
||||
debug("Created Kernel Process: %s and Thread: %s",
|
||||
kproc->Name, kthrd->Name);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
IdleProcess = CreateProcess(nullptr, (char *)"Idle", TaskExecutionMode::Kernel);
|
||||
IdleProcess->ELFSymbolTable = KernelSymbolTable;
|
||||
for (int i = 0; i < SMP::CPUCores; i++)
|
||||
{
|
||||
IdleThread = CreateThread(IdleProcess, IP(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(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. */
|
||||
// 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(aa64)
|
||||
#endif
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
debug("Destructor called");
|
||||
{
|
||||
SmartLock(TaskingLock);
|
||||
foreach (PCB *Process in ProcessList)
|
||||
{
|
||||
foreach (TCB *Thread in Process->Threads)
|
||||
{
|
||||
if (Thread == GetCurrentCPU()->CurrentThread.load() ||
|
||||
Thread == CleanupThread)
|
||||
continue;
|
||||
this->KillThread(Thread, KILL_SCHEDULER_DESTRUCTION);
|
||||
}
|
||||
|
||||
if (Process == GetCurrentCPU()->CurrentProcess.load())
|
||||
continue;
|
||||
this->KillProcess(Process, KILL_SCHEDULER_DESTRUCTION);
|
||||
}
|
||||
}
|
||||
|
||||
while (ProcessList.size() > 0)
|
||||
{
|
||||
trace("Waiting for %d processes to terminate", ProcessList.size());
|
||||
int NotTerminated = 0;
|
||||
foreach (PCB *Process in ProcessList)
|
||||
{
|
||||
debug("Process %s(%d) is still running (or waiting to be removed status %#lx)",
|
||||
Process->Name, Process->ID, Process->State);
|
||||
if (Process->State == TaskState::Terminated)
|
||||
continue;
|
||||
NotTerminated++;
|
||||
}
|
||||
if (NotTerminated == 1)
|
||||
break;
|
||||
TaskingScheduler_OneShot(100);
|
||||
}
|
||||
|
||||
debug("Tasking stopped");
|
||||
}
|
||||
}
|
548
tasking/thread.cpp
Normal file
548
tasking/thread.cpp
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
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->State = Tasking::TaskState::Terminated;
|
||||
|
||||
debug("\"%s\"(%d) exited with code: %#lx",
|
||||
CurrentThread->Name,
|
||||
CurrentThread->ID,
|
||||
CurrentThread->ExitCode);
|
||||
CPU::Halt(true);
|
||||
}
|
||||
|
||||
namespace Tasking
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void TCB::SetDebugMode(bool Enable)
|
||||
{
|
||||
trace("Setting debug mode of thread %s to %s",
|
||||
this->Name, Enable ? "true" : "false");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 *)this->Stack->GetStackPhysicalTop();
|
||||
// Temporary stack pointer for strings
|
||||
char *StackStrings = (char *)Stack;
|
||||
char *StackStringsVirtual = (char *)this->Stack->GetStackTop();
|
||||
|
||||
// Store string pointers for later
|
||||
uintptr_t *ArgvStrings = nullptr;
|
||||
uintptr_t *EnvpStrings = nullptr;
|
||||
if (ArgvSize > 0)
|
||||
ArgvStrings = new uintptr_t[ArgvSize];
|
||||
if (EnvpSize > 0)
|
||||
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]);
|
||||
debug("argv[%d]: %s", i, 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]);
|
||||
debug("envp[%d]: %s", i, 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 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
|
||||
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)this->Stack->GetStackPhysicalTop() - (uintptr_t)Stack;
|
||||
debug("SubtractStack: %#lx", SubtractStack);
|
||||
|
||||
// Set the stack pointer to the new stack
|
||||
uintptr_t StackPointerReg = ((uintptr_t)this->Stack->GetStackTop() - SubtractStack);
|
||||
#if defined(a64)
|
||||
this->Registers.rsp = StackPointerReg;
|
||||
#elif defined(a32)
|
||||
this->Registers.esp = StackPointerReg;
|
||||
#elif defined(aa64)
|
||||
this->Registers.sp = StackPointerReg;
|
||||
#endif
|
||||
|
||||
if (ArgvSize > 0)
|
||||
delete[] ArgvStrings;
|
||||
if (EnvpSize > 0)
|
||||
delete[] EnvpStrings;
|
||||
|
||||
#ifdef DEBUG
|
||||
DumpData("Stack Data", (void *)((uintptr_t)this->Stack->GetStackPhysicalTop() - (uintptr_t)SubtractStack), SubtractStack);
|
||||
#endif
|
||||
|
||||
#if defined(a64)
|
||||
this->Registers.rdi = (uintptr_t)ArgvSize; // argc
|
||||
this->Registers.rsi = (uintptr_t)(this->Registers.rsp + 8); // argv
|
||||
this->Registers.rcx = (uintptr_t)EnvpSize; // envc
|
||||
this->Registers.rdx = (uintptr_t)(this->Registers.rsp + 8 + (8 * ArgvSize) + 8); // envp
|
||||
#elif defined(a32)
|
||||
this->Registers.eax = (uintptr_t)ArgvSize; // argc
|
||||
this->Registers.ebx = (uintptr_t)(this->Registers.esp + 4); // argv
|
||||
this->Registers.ecx = (uintptr_t)EnvpSize; // envc
|
||||
this->Registers.edx = (uintptr_t)(this->Registers.esp + 4 + (4 * ArgvSize) + 4); // envp
|
||||
#elif defined(aa64)
|
||||
this->Registers.x0 = (uintptr_t)ArgvSize; // argc
|
||||
this->Registers.x1 = (uintptr_t)(this->Registers.sp + 8); // argv
|
||||
this->Registers.x2 = (uintptr_t)EnvpSize; // envc
|
||||
this->Registers.x3 = (uintptr_t)(this->Registers.sp + 8 + (8 * ArgvSize) + 8); // envp
|
||||
#endif
|
||||
}
|
||||
|
||||
void TCB::SetupUserStack_x86_32(const char **argv,
|
||||
const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv)
|
||||
{
|
||||
fixme("Not implemented");
|
||||
}
|
||||
|
||||
void TCB::SetupUserStack_aarch64(const char **argv,
|
||||
const char **envp,
|
||||
const std::vector<AuxiliaryVector> &auxv)
|
||||
{
|
||||
fixme("Not implemented");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 = ctx->NextTID++;
|
||||
|
||||
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->State = TaskState::Waiting;
|
||||
else
|
||||
this->State = TaskState::Ready;
|
||||
|
||||
this->vma = new Memory::VirtualMemoryArea(this->Parent->PageTable);
|
||||
|
||||
#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.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.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);
|
||||
|
||||
gsTCB *gsT = (gsTCB *)this->vma->RequestPages(TO_PAGES(sizeof(gsTCB)));
|
||||
|
||||
gsT->ScPages = TO_PAGES(STACK_SIZE);
|
||||
gsT->SyscallStackBase = this->vma->RequestPages(gsT->ScPages);
|
||||
gsT->SyscallStack = (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;
|
||||
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;
|
||||
/* 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);
|
||||
#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;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
this->Info.Architecture = Architecture;
|
||||
this->Info.Compatibility = Compatibility;
|
||||
this->Security.ExecutionMode =
|
||||
this->Parent->Security.ExecutionMode;
|
||||
|
||||
// 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->State = 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 */
|
||||
std::vector<Tasking::TCB *> &Threads = this->Parent->Threads;
|
||||
Threads.erase(std::find(Threads.begin(),
|
||||
Threads.end(),
|
||||
this));
|
||||
|
||||
/* Free CPU Stack */
|
||||
delete this->Stack;
|
||||
|
||||
/* Free all allocated memory */
|
||||
delete this->vma;
|
||||
|
||||
/* Free Name */
|
||||
delete[] this->Name;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user