summaryrefslogtreecommitdiffhomepage
path: root/dev/ZKA/Sources/UserProcessScheduler.cxx
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2024-08-31 10:47:14 +0200
committerAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2024-08-31 10:47:14 +0200
commit2c4b02249ec4355a73b826909ab1889e45871faf (patch)
treea904b374c7a738dd3dcb0a580e5667714b72682a /dev/ZKA/Sources/UserProcessScheduler.cxx
parentd65e85588388bf8c2e0dc8f6b106947110c0e815 (diff)
Saving progress on User scheduler implementation.
+ Working on RISC-V, ARM64, POWER64 HALs, to be able to distribute mulitple versions of the product. Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'dev/ZKA/Sources/UserProcessScheduler.cxx')
-rw-r--r--dev/ZKA/Sources/UserProcessScheduler.cxx480
1 files changed, 480 insertions, 0 deletions
diff --git a/dev/ZKA/Sources/UserProcessScheduler.cxx b/dev/ZKA/Sources/UserProcessScheduler.cxx
new file mode 100644
index 00000000..94512913
--- /dev/null
+++ b/dev/ZKA/Sources/UserProcessScheduler.cxx
@@ -0,0 +1,480 @@
+/* -------------------------------------------
+
+ Copyright ZKA Technologies.
+
+------------------------------------------- */
+
+/***********************************************************************************/
+/// @file UserProcessScheduler.cxx
+/// @brief User UserProcess scheduler.
+/***********************************************************************************/
+
+#include <KernelKit/UserProcessScheduler.hxx>
+#include <KernelKit/PEFDLLInterface.hxx>
+#include <KernelKit/MP.hxx>
+#include <KernelKit/Heap.hxx>
+#include <NewKit/String.hxx>
+#include <KernelKit/LPC.hxx>
+
+///! BUGS: 0
+
+/***********************************************************************************/
+/* TODO: Document more the Kernel, sdk and kits. */
+/***********************************************************************************/
+
+namespace Kernel
+{
+ /***********************************************************************************/
+ /// @brief Exit Code global variable.
+ /***********************************************************************************/
+
+ UInt32 cLastExitCode = 0U;
+
+ /***********************************************************************************/
+ /// @brief UserProcess scheduler instance.
+ /***********************************************************************************/
+
+ UserProcessScheduler* cProcessScheduler = nullptr;
+
+ /// @brief Gets the last exit code.
+ /// @note Not thread-safe.
+ /// @return Int32 the last exit code.
+ const UInt32& sched_get_exit_code(void) noexcept
+ {
+ return cLastExitCode;
+ }
+
+ /***********************************************************************************/
+ /// @brief crash current process.
+ /***********************************************************************************/
+
+ void UserProcess::Crash()
+ {
+ constexpr auto cUnknownProcess = "?";
+
+ kcout << (*this->Name == 0 ? cUnknownProcess : this->Name) << ": crashed. (id = " << number(kErrorProcessFault);
+ kcout << ")\r";
+
+ this->Status = ProcessStatus::kDead;
+
+ this->Exit(kErrorProcessFault);
+ }
+
+ /// @brief Gets the local last exit code.
+ /// @note Not thread-safe.
+ /// @return Int32 the last exit code.
+ const UInt32& UserProcess::GetExitCode() noexcept
+ {
+ return this->fLastExitCode;
+ }
+
+ /***********************************************************************************/
+ /// @brief Error code variable getter.
+ /***********************************************************************************/
+
+ Int32& UserProcess::GetLocalCode() noexcept
+ {
+ return fLocalCode;
+ }
+
+ void UserProcess::Wake(const bool should_wakeup)
+ {
+ this->Status =
+ should_wakeup ? ProcessStatus::kRunning : ProcessStatus::kFrozen;
+ }
+
+ /***********************************************************************************/
+
+ VoidPtr UserProcess::New(const SizeT& sz)
+ {
+ if (this->HeapCursor)
+ {
+ if (this->FreeMemory < 1)
+ {
+ ErrLocal() = kErrorHeapOutOfMemory;
+
+ /* We're going out of memory! crash... */
+ this->Crash();
+
+ return nullptr;
+ }
+
+ this->HeapCursor = reinterpret_cast<VoidPtr>((UIntPtr)this->HeapCursor + (sizeof(sz)));
+ VoidPtr cursor = this->HeapCursor;
+
+ ++this->UsedMemory;
+ --this->FreeMemory;
+
+ return cursor;
+ }
+
+ return nullptr;
+ }
+
+ /***********************************************************************************/
+
+ /* @brief checks if runtime pointer is in region. */
+ bool rt_is_in_pool(VoidPtr pool_ptr, VoidPtr pool, const SizeT& pool_ptr_cur_sz, const SizeT& pool_ptr_used_sz)
+ {
+ if (pool == nullptr ||
+ pool_ptr == nullptr)
+ return false;
+
+ UIntPtr* uint_pool_ptr = (UIntPtr*)pool_ptr;
+ UIntPtr* uint_pool = (UIntPtr*)pool;
+
+ return (UIntPtr)&uint_pool > (UIntPtr)&uint_pool_ptr &&
+ pool_ptr_cur_sz > pool_ptr_used_sz;
+ }
+
+ /* @brief free pointer from usage. */
+ Boolean UserProcess::Delete(VoidPtr ptr, const SizeT& sz)
+ {
+ if (sz < 1 || this->HeapCursor == this->HeapPtr)
+ return false;
+
+ // also check for the amount of allocations we've done so far.
+ if (this->UsedMemory < 1)
+ return false;
+
+ if (rt_is_in_pool(ptr, this->HeapCursor, this->UsedMemory, this->FreeMemory))
+ {
+ this->HeapCursor = (VoidPtr)((UIntPtr)this->HeapCursor - (sizeof(sz)));
+ rt_zero_memory(ptr, sz);
+
+ ++this->FreeMemory;
+ --this->UsedMemory;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /// @brief UserProcess name getter.
+ const Char* UserProcess::GetProcessName() noexcept
+ {
+ return this->Name;
+ }
+
+ /// @brief UserProcess user getter.
+ const User* UserProcess::GetOwner() noexcept
+ {
+ return this->Owner;
+ }
+
+ /// @brief UserProcess status getter.
+ const ProcessStatus& UserProcess::GetStatus() noexcept
+ {
+ return this->Status;
+ }
+
+ /***********************************************************************************/
+
+ /**
+ @brief Affinity is the time slot allowed for the process.
+ */
+ const AffinityKind& UserProcess::GetAffinity() noexcept
+ {
+ return this->Affinity;
+ }
+
+ /**
+ @brief Standard exit proc.
+ */
+ void UserProcess::Exit(const Int32& exit_code)
+ {
+ fLastExitCode = exit_code;
+ cLastExitCode = exit_code;
+
+ //! Delete image if not done already.
+ if (this->Image)
+ mm_delete_ke_heap(this->Image);
+
+ if (this->StackFrame)
+ mm_delete_ke_heap((VoidPtr)this->StackFrame);
+
+ this->Image = nullptr;
+ this->StackFrame = nullptr;
+
+ if (this->Kind == kDLLKind)
+ {
+ Bool success = false;
+ rtl_fini_shared_object(this, this->DLLPtr, &success);
+
+ if (success)
+ {
+ this->DLLPtr = nullptr;
+ }
+ }
+
+ if (this->StackReserve)
+ delete[] this->StackReserve;
+
+ cProcessScheduler->Remove(this->ProcessId);
+ }
+
+ /// @brief Add process to list.
+ /// @param process the process *Ref* class.
+ /// @return the process index inside the team.
+ SizeT UserProcessScheduler::Add(UserProcess& process)
+ {
+ if (!process.Image)
+ {
+ return -kErrorInvalidData;
+ }
+
+ kcout << "UserProcessScheduler: Adding process to team...\r";
+
+ // Create heap according to type of process.
+ if (process.Kind == UserProcess::kExeKind)
+ {
+ process.HeapPtr = mm_new_ke_heap(process.SizeMemory, true, true);
+ }
+ else if (process.Kind == UserProcess::kDLLKind)
+ {
+ process.DLLPtr = rtl_init_shared_object(&process);
+ process.HeapPtr = mm_new_ke_heap(process.SizeMemory, true, true);
+ }
+ else
+ {
+ // Something went wrong, do not continue, process may be incorrect.
+ process.Crash();
+ return -kErrorProcessFault;
+ }
+
+ process.StackFrame = reinterpret_cast<HAL::StackFrame*>(
+ mm_new_ke_heap(sizeof(HAL::StackFrame), Yes, Yes));
+
+ MUST_PASS(process.StackFrame);
+
+ if (process.Image)
+ {
+ // get preferred stack size by app.
+ const auto cMaxStackSize = process.StackSize;
+
+ process.StackFrame->BP = reinterpret_cast<HAL::Reg>(process.Image);
+ process.StackFrame->SP = reinterpret_cast<HAL::Reg>(mm_new_ke_heap(cMaxStackSize, Yes, Yes));
+
+ if (!process.StackFrame->SP)
+ {
+ process.StackReserve = new UInt8[cMaxStackSz];
+ process.StackFrame->SP = reinterpret_cast<HAL::Reg>(process.StackReserve);
+
+ kcout << "newoskrnl: use fallback reserve.\r";
+ }
+ }
+ else
+ {
+ if (process.Kind != UserProcess::kDLLKind)
+ {
+ process.Crash();
+ return -kErrorProcessFault;
+ }
+ }
+
+ process.Status = ProcessStatus::kStarting;
+
+ process.ProcessId = mTeam.mProcessAmount;
+
+ ++mTeam.mProcessAmount;
+
+ process.HeapCursor = process.HeapPtr;
+
+ mTeam.AsArray()[process.ProcessId] = process;
+
+ kcout << "UserProcessScheduler: Adding process to team [ OK ]...\r";
+
+ return process.ProcessId;
+ }
+
+ /***********************************************************************************/
+
+ UserProcessScheduler& UserProcessScheduler::The()
+ {
+ MUST_PASS(cProcessScheduler);
+ return *cProcessScheduler;
+ }
+
+ /***********************************************************************************/
+
+ /// @brief Remove process from list.
+ /// @param processSlot process slot inside team.
+ /// @retval true process was removed.
+ /// @retval false process doesn't exist in team.
+ Bool UserProcessScheduler::Remove(ProcessID processSlot)
+ {
+ // check if process is within range.
+ if (processSlot > mTeam.AsArray().Count())
+ return false;
+
+ // also check if the process isn't a dummy one.
+ if (mTeam.AsArray()[processSlot].Image == nullptr)
+ return false;
+
+ kcout << "UserProcessScheduler: Removing process...\r";
+
+ mTeam.AsArray()[processSlot].Status = ProcessStatus::kDead;
+ --mTeam.mProcessAmount;
+
+ return true;
+ }
+
+ /// @brief Run scheduler.
+ /// @return
+ SizeT UserProcessScheduler::Run() noexcept
+ {
+ SizeT process_index = 0; //! we store this guy to tell the scheduler how many
+ //! things we have scheduled.
+
+ for (; process_index < mTeam.AsArray().Capacity(); ++process_index)
+ {
+ auto& process = mTeam.AsArray()[process_index];
+
+ //! check if process needs to be scheduled.
+ if (ProcessHelper::CanBeScheduled(process))
+ {
+ // set the current process.
+ mTeam.AsRef() = process;
+
+ process.PTime = static_cast<Int32>(process.Affinity);
+
+ kcout << process.Name << ": will be runned.\r";
+
+ // tell helper to find a core to schedule on.
+ if (!ProcessHelper::Switch(process.StackFrame,
+ process.ProcessId))
+ process.Crash();
+
+ continue;
+ }
+ else
+ {
+ // otherwise increment the P-time.
+ --process.PTime;
+ }
+ }
+
+ return process_index;
+ }
+
+ /// @brief Gets the current scheduled team.
+ /// @return
+ ProcessTeam& UserProcessScheduler::CurrentTeam()
+ {
+ return mTeam;
+ }
+
+ /// @internal
+
+ /// @brief Gets current running process.
+ /// @return
+ Ref<UserProcess>& UserProcessScheduler::CurrentProcess()
+ {
+ return mTeam.AsRef();
+ }
+
+ /// @brief Current proccess id getter.
+ /// @return UserProcess ID integer.
+ PID& ProcessHelper::TheCurrentPID()
+ {
+ kcout << "ProcessHelper::TheCurrentPID: Leaking ProcessId...\r";
+ return cProcessScheduler->CurrentProcess().Leak().ProcessId;
+ }
+
+ /// @brief Check if process can be schedulded.
+ /// @param process the process reference.
+ /// @retval true can be schedulded.
+ /// @retval false cannot be schedulded.
+ bool ProcessHelper::CanBeScheduled(UserProcess& process)
+ {
+ if (process.Status == ProcessStatus::kFrozen ||
+ process.Status == ProcessStatus::kDead)
+ return false;
+
+ if (process.Kind == UserProcess::kDLLKind)
+ {
+ if (auto start = process.DLLPtr->Load<VoidPtr>(kPefStart, rt_string_len(kPefStart), kPefCode);
+ start)
+ {
+ process.Image = start;
+ process.StackFrame->BP = reinterpret_cast<HAL::Reg>(start);
+ }
+ }
+
+ return process.PTime < 1;
+ }
+
+ /**
+ * @brief Scheduler helper class.
+ */
+
+ SizeT ProcessHelper::StartScheduling()
+ {
+ if (!cProcessScheduler)
+ {
+ cProcessScheduler = new UserProcessScheduler();
+ MUST_PASS(cProcessScheduler);
+
+ kcout << "newoskrnl: Team capacity: " << number(cProcessScheduler->CurrentTeam().AsArray().Capacity()) << endl;
+ }
+
+ SizeT ret = cProcessScheduler->Run();
+ return ret;
+ }
+
+ /**
+ * \brief Does a context switch in a CPU.
+ * \param the_stack the stackframe of the running app.
+ * \param new_pid the process's PID.
+ */
+
+ bool ProcessHelper::Switch(HAL::StackFrame* the_stack, const PID& new_pid)
+ {
+ if (!the_stack || new_pid < 0)
+ return false;
+
+ kcout << "newoskrnl: Finding hardware thread...\r";
+
+ for (SizeT index = 0UL; index < HardwareThreadScheduler::The().Count(); ++index)
+ {
+ if (HardwareThreadScheduler::The()[index].Leak()->Kind() == kInvalidHart)
+ continue;
+
+ if (HardwareThreadScheduler::The()[index].Leak()->IsBusy())
+ continue;
+
+ if (HardwareThreadScheduler::The()[index].Leak()->Kind() !=
+ ThreadKind::kHartBoot &&
+ HardwareThreadScheduler::The()[index].Leak()->Kind() !=
+ ThreadKind::kHartSystemReserved)
+ {
+ HardwareThreadScheduler::The()[index].Leak()->Busy(true);
+
+ ProcessHelper::TheCurrentPID() = new_pid;
+
+ kcout << "newoskrnl: Found hardware thread...\r";
+
+ bool ret = HardwareThreadScheduler::The()[index].Leak()->Switch(the_stack);
+
+ HardwareThreadScheduler::The()[index].Leak()->Busy(false);
+
+ return ret;
+ }
+ }
+
+ return false;
+ }
+
+ /// @brief this checks if any process is on the team.
+ UserProcessScheduler::operator bool()
+ {
+ return mTeam.AsArray().Count() > 0;
+ }
+
+ /// @brief this checks if no process is on the team.
+ bool UserProcessScheduler::operator!()
+ {
+ return mTeam.AsArray().Count() == 0;
+ }
+} // namespace Kernel