diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 3726e05d..ac6315b5 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -60,6 +60,7 @@ private: void switchToNextThread(); void rescheduleThreads(); bool canThreadRun(const Thread& t); + bool shouldWaitOnObject(KernelObject* object); std::optional getPortHandle(const char* name); void deleteObjectData(KernelObject& object); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 93a1f47d..282d554a 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -121,8 +121,12 @@ struct Thread { u64 waitingNanoseconds; // The tick this thread went to sleep on u64 sleepTick; - // For WaitSynchronization: A vector of objects this thread is waiting for + // For WaitSynchronization(N): A vector of objects this thread is waiting for std::vector waitList; + // For WaitSynchronizationN: Shows whether the object should wait for all objects in the wait list or just one + bool waitAll; + // For WaitSynchronizationN: The "out" pointer + u32 outPointer; // 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 c7ab1c92..3c12ee2c 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -37,6 +37,7 @@ void Kernel::clearEvent() { logSVC("ClearEvent(event handle = %X)\n", handle); if (event == nullptr) [[unlikely]] { + Helpers::panic("Tried to clear non-existent event"); regs[0] = SVCResult::BadHandle; return; } @@ -51,17 +52,35 @@ void Kernel::signalEvent() { const auto event = getObject(handle, KernelObjectType::Event); logSVC("SignalEvent(event handle = %X)\n", handle); printf("Stubbed SignalEvent!!\n"); - - /* - if (event == nullptr) [[unlikely]] { - regs[0] = SVCResult::BadHandle; - return; - } - - event->getData()->fired = true; - */ + + if (event == nullptr) [[unlikely]] { + Helpers::warn("Signalled non-existent event: %X\n", handle); + regs[0] = SVCResult::Success; + //regs[0] = SVCResult::BadHandle; + return; + } regs[0] = SVCResult::Success; + auto eventData = event->getData(); + eventData->fired = true; + + switch (eventData->resetType) { + case ResetType::OneShot: + for (int i = 0; i < threadCount; i++) { + Thread& t = threads[i]; + + if (t.status == ThreadStatus::WaitSync1 && t.waitList[0] == handle) { + t.status = ThreadStatus::Ready; + break; + } else if (t.status == ThreadStatus::WaitSyncAll) { + Helpers::panic("Trying to SignalEvent when a thread is waiting on multiple objects"); + } + } + break; + + default: + Helpers::panic("Signaled event of unimplemented type: %d", eventData->resetType); + } } // Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds) @@ -82,15 +101,19 @@ void Kernel::waitSynchronization1() { Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); } - regs[0] = SVCResult::Success; + if (!shouldWaitOnObject(object)) { + regs[0] = SVCResult::Success; + } else { + 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(); + 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) @@ -101,11 +124,13 @@ void Kernel::waitSynchronizationN() { u32 handleCount = regs[2]; bool waitAll = regs[3] != 0; u32 ns2 = regs[4]; - s32 pointer = regs[5]; + s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects s64 ns = s64(ns1) | (s64(ns2) << 32); logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns); - ThreadStatus newStatus = waitAll ? ThreadStatus::WaitSyncAll : ThreadStatus::WaitSync1; + + if (waitAll && handleCount > 1) + Helpers::panic("Trying to wait on more than 1 object"); auto& t = threads[currentThreadIndex]; t.waitList.resize(handleCount); @@ -124,12 +149,14 @@ void Kernel::waitSynchronizationN() { } if (!isWaitable(object)) [[unlikely]] { - Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); + //Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); } } regs[0] = SVCResult::Success; - t.status = newStatus; + t.status = ThreadStatus::WaitSyncAll; + t.waitAll = waitAll; + t.outPointer = outPointer; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); switchToNextThread(); diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index e182dbc0..2725865f 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -10,10 +10,15 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) threadIndices.reserve(appResourceLimits.maxThreads); for (int i = 0; i < threads.size(); i++) { - 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 + Thread& t = threads[i]; + + t.index = i; + t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize; + t.status = ThreadStatus::Dead; + t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later + // The state below isn't necessary to initialize but we do it anyways out of caution + t.outPointer = 0; + t.waitAll = false; } setVersion(1, 69); diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 4db5c2a8..7f686783 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -82,8 +82,8 @@ void Kernel::switchToNextThread() { std::optional newThreadIndex = getNextThread(); if (!newThreadIndex.has_value()) { - Helpers::warn("Kernel tried to switch to the next thread but none found. Switching to thread 0\n"); - switchThread(0); + Helpers::warn("Kernel tried to switch to the next thread but none found. Switching to random thread\n"); + switchThread(rand() % threadCount); } else { switchThread(newThreadIndex.value()); } @@ -301,4 +301,16 @@ bool Kernel::isWaitable(const KernelObject* object) { using enum KernelObjectType; return type == Event || type == Mutex || type == Port || type == Semaphore || type == Timer || type == Thread; +} + +// Returns whether we should wait on a sync object or not +bool Kernel::shouldWaitOnObject(KernelObject* object) { + switch (object->type) { + case KernelObjectType::Event: // We should wait on an event only if it has not been signalled + return !object->getData()->fired; + + default: + Helpers::warn("Not sure whether to wait on object (type: %s)", object->getTypeName()); + return true; + } } \ No newline at end of file diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4ae90885..12ebeeaf 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -117,6 +117,11 @@ u32 Memory::read32(u32 vaddr) { case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit; case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does default: + if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { + Helpers::warn("VRAM read!\n"); + return 0; + } + Helpers::panic("Unimplemented 32-bit read, addr: %08X", vaddr); break; }