From 41e01bbdd4f5e44df7acc62073af3401a8b38416 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Wed, 16 Nov 2022 22:02:52 +0200 Subject: [PATCH] Sync objects 0.1 --- include/kernel/kernel.hpp | 2 ++ include/kernel/kernel_types.hpp | 6 ++-- src/core/kernel/events.cpp | 58 ++++++++++++++++++++++++--------- src/core/kernel/kernel.cpp | 2 ++ src/core/kernel/threads.cpp | 15 +++++++-- src/core/services/apt.cpp | 4 +-- 6 files changed, 66 insertions(+), 21 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 8e16b4fb..9f9dfc33 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -64,6 +64,8 @@ class Kernel { u32 getMaxForResource(const KernelObject* limit, u32 resourceName); u32 getTLSPointer(); + bool isWaitable(const KernelObject* object); + // Functions for the err:f port void handleErrorSyncRequest(u32 messagePointer); void throwError(u32 messagePointer); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index e66b7e44..e0547cfb 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -23,9 +23,9 @@ namespace SVCResult { } enum class KernelObjectType : u8 { - AddressArbiter, Archive, File, Port, Process, ResourceLimit, Session, Dummy, + AddressArbiter, Archive, File, Process, ResourceLimit, Session, Dummy, // Bundle waitable objects together in the enum to let the compiler optimize certain checks better - Event, Mutex, Semaphore, Thread + Event, Mutex, Port, Semaphore, Timer, Thread }; enum class ResourceLimitCategory : int { @@ -121,6 +121,8 @@ struct Thread { s64 waitingNanoseconds; // The tick this thread went to sleep on u64 sleepTick; + // For WaitSynchronization: A vector of objects this thread is waiting for + std::vector waitList; // Thread context used for switching between threads std::array gprs; diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 3d69d059..f3132473 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -1,4 +1,5 @@ #include "kernel.hpp" +#include "cpu.hpp" const char* Kernel::resetTypeToString(u32 type) { switch (type) { @@ -67,41 +68,68 @@ void Kernel::signalEvent() { void Kernel::waitSynchronization1() { const Handle handle = regs[0]; const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32)); - const auto event = getObject(handle, KernelObjectType::Event); + logSVC("WaitSynchronization1(handle = %X, ns = %lld)\n", handle, ns); - if (event == nullptr) [[unlikely]] { + const auto object = getObject(handle); + + if (object == nullptr) [[unlikely]] { Helpers::panic("WaitSynchronization1: Bad event handle"); regs[0] = SVCResult::BadHandle; return; } - logSVC("WaitSynchronization1(handle = %X, ns = %lld) (STUBBED)\n", handle, ns); + if (!isWaitable(object)) [[unlikely]] { + Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", kernelObjectTypeToString(object->type), handle); + } + regs[0] = SVCResult::Success; + + auto& t = threads[currentThreadIndex]; + t.waitList.resize(1); + t.status = ThreadStatus::WaitSync1; + t.sleepTick = cpu.getTicks(); + t.waitingNanoseconds = ns; + t.waitList[0] = handle; + switchToNextThread(); } // Result WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds) void Kernel::waitSynchronizationN() { // TODO: Are these arguments even correct? - u32 ns1 = regs[0]; + s32 ns1 = regs[0]; u32 handles = regs[1]; u32 handleCount = regs[2]; bool waitAll = regs[3] != 0; u32 ns2 = regs[4]; - u32 out = regs[5]; + s32 pointer = regs[5]; + s64 ns = s64(ns1) | (s64(ns2) << 32); - logSVC("WaitSynchronizationN (STUBBED)\n"); - regs[0] = SVCResult::Success; + logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d)\n", handles, handleCount); + ThreadStatus newStatus = waitAll ? ThreadStatus::WaitSyncAll : ThreadStatus::WaitSync1; - printf("Hacky WaitSync stuff for OoT triggered!!!\n"); - threads[currentThreadIndex].status = ThreadStatus::Ready; + auto& t = threads[currentThreadIndex]; + t.waitList.resize(handleCount); + + for (uint i = 0; i < handleCount; i++) { + Handle handle = mem.read32(handles); + handles += sizeof(Handle); - while (1) { - auto index = rand() % threadCount; - auto& thread = threads[index]; + t.waitList[i] = handle; - if (canThreadRun(thread)) { - switchThread(rand() % threadCount); - break; + auto object = getObject(handle); + if (object == nullptr) [[unlikely]] { + Helpers::panic("WaitSynchronizationN: Bad object handle"); + regs[0] = SVCResult::BadHandle; + return; + } + + if (!isWaitable(object)) [[unlikely]] { + Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", kernelObjectTypeToString(object->type), handle); } } + + regs[0] = SVCResult::Success; + t.status = newStatus; + t.waitingNanoseconds = ns; + switchToNextThread(); } \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 9552199d..e182dbc0 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -13,6 +13,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) threads[i].index = i; threads[i].tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize; threads[i].status = ThreadStatus::Dead; + threads[i].waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later } setVersion(1, 69); @@ -101,6 +102,7 @@ void Kernel::reset() { for (auto& t : threads) { t.status = ThreadStatus::Dead; + t.waitList.clear(); } for (auto& object : objects) { diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 94443af1..ff4ae9e6 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -46,7 +46,7 @@ void Kernel::sortThreads() { bool Kernel::canThreadRun(const Thread& t) { if (t.status == ThreadStatus::Ready) { return true; - } else if (t.status == ThreadStatus::WaitSleep) { + } else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAll) { const u64 elapsedTicks = cpu.getTicks() - t.sleepTick; constexpr double ticksPerSec = double(CPU::ticksPerSec); @@ -260,9 +260,10 @@ void Kernel::setThreadPriority() { void Kernel::createMutex() { bool locked = regs[1] != 0; - Helpers::panic("CreateMutex (initially locked: %s)\n", locked ? "yes" : "no"); +// Helpers::panic("CreateMutex (initially locked: %s)\n", locked ? "yes" : "no"); regs[0] = SVCResult::Success; + regs[1] = makeObject(KernelObjectType::Mutex); } void Kernel::releaseMutex() { @@ -270,4 +271,14 @@ void Kernel::releaseMutex() { logSVC("ReleaseMutex (handle = %x) (STUBBED)\n", handle); regs[0] = SVCResult::Success; +} + +// Returns whether an object is waitable or not +// The KernelObject type enum is arranged in a specific order in kernel_types.hpp so this +// can simply compile to a fast sub+cmp+set despite looking slow +bool Kernel::isWaitable(const KernelObject* object) { + auto type = object->type; + using enum KernelObjectType; + + return type == Event || type == Mutex || type == Port || type == Semaphore || type == Timer || type == Thread; } \ No newline at end of file diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 26a96692..5e41c7b7 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -80,8 +80,8 @@ void APTService::enable(u32 messagePointer) { } void APTService::getLockHandle(u32 messagePointer) { - log("APT::GetLockHandle (Failure)\n"); - mem.write32(messagePointer + 4, Result::Failure); // Result code + log("APT::GetLockHandle"); + mem.write32(messagePointer + 4, Result::Success); // Result code mem.write32(messagePointer + 8, 0); // AppletAttr mem.write32(messagePointer + 12, 0); // APT State (bit0 = Power Button State, bit1 = Order To Close State) mem.write32(messagePointer + 16, 0); // Translation descriptor