#include "kernel.hpp" #include "cpu.hpp" #include #include const char* Kernel::resetTypeToString(u32 type) { switch (type) { case 0: return "One shot"; case 1: return "Sticky"; case 2: return "Pulse"; default: return "Invalid"; } } Handle Kernel::makeEvent(ResetType resetType) { Handle ret = makeObject(KernelObjectType::Event); objects[ret].data = new Event(resetType); return ret; } bool Kernel::signalEvent(Handle handle) { KernelObject* object = getObject(handle, KernelObjectType::Event); if (object == nullptr) [[unlikely]] { Helpers::panic("Tried to signal non-existent event"); return false; } Event* event = object->getData(); event->fired = true; // One shot events go back to being not fired once they are signaled if (event->resetType == ResetType::Pulse) { event->fired = false; } // Check if there's any thread waiting on this event if (event->waitlist != 0) { wakeupAllThreads(event->waitlist, handle); event->waitlist = 0; // No threads waiting; if (event->resetType == ResetType::OneShot) { event->fired = false; } } rescheduleThreads(); return true; } // Result CreateEvent(Handle* event, ResetType resetType) void Kernel::svcCreateEvent() { const u32 outPointer = regs[0]; const u32 resetType = regs[1]; if (resetType > 2) Helpers::panic("Invalid reset type for event %d", resetType); logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType)); regs[0] = Result::Success; regs[1] = makeEvent(static_cast(resetType)); } // Result ClearEvent(Handle event) void Kernel::svcClearEvent() { const Handle handle = regs[0]; const auto event = getObject(handle, KernelObjectType::Event); logSVC("ClearEvent(event handle = %X)\n", handle); if (event == nullptr) [[unlikely]] { Helpers::panic("Tried to clear non-existent event (handle = %X)", handle); regs[0] = Result::Kernel::InvalidHandle; return; } event->getData()->fired = false; regs[0] = Result::Success; } // Result SignalEvent(Handle event) void Kernel::svcSignalEvent() { const Handle handle = regs[0]; logSVC("SignalEvent(event handle = %X)\n", handle); KernelObject* object = getObject(handle, KernelObjectType::Event); if (object == nullptr) { Helpers::panic("Signalled non-existent event: %X\n", handle); regs[0] = Result::Kernel::InvalidHandle; } else { // We must signalEvent after setting r0, otherwise the r0 of the new thread will ne corrupted regs[0] = Result::Success; signalEvent(handle); } } // Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds) void Kernel::waitSynchronization1() { const Handle handle = regs[0]; const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32)); logSVC("WaitSynchronization1(handle = %X, ns = %lld)\n", handle, ns); const auto object = getObject(handle); if (object == nullptr) [[unlikely]] { Helpers::panic("WaitSynchronization1: Bad event handle %X\n", handle); regs[0] = Result::Kernel::InvalidHandle; return; } if (!isWaitable(object)) [[unlikely]] { Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); } if (!shouldWaitOnObject(object)) { acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready regs[0] = Result::Success; } else { // Timeout is 0, don't bother waiting, instantly timeout if (ns == 0) { regs[0] = Result::OS::Timeout; return; } regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout auto& t = threads[currentThreadIndex]; t.waitList.resize(1); t.status = ThreadStatus::WaitSync1; t.sleepTick = cpu.getTicks(); t.waitingNanoseconds = ns; t.waitList[0] = handle; // Add the current thread to the object's wait list object->getWaitlist() |= (1ull << currentThreadIndex); requireReschedule(); } } // Result WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds) void Kernel::waitSynchronizationN() { // TODO: Are these arguments even correct? s32 ns1 = regs[0]; u32 handles = regs[1]; s32 handleCount = regs[2]; bool waitAll = regs[3] != 0; u32 ns2 = regs[4]; 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); if (handleCount <= 0) Helpers::panic("WaitSyncN: Invalid handle count"); using WaitObject = std::pair; std::vector waitObjects(handleCount); // We don't actually need to wait if waitAll == true unless one of the objects is not ready bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready // Tracks whether at least one object is ready, + the index of the first ready object // This is used when waitAll == false, because if one object is already available then we can skip the sleeping bool oneObjectReady = false; s32 firstReadyObjectIndex = 0; for (s32 i = 0; i < handleCount; i++) { Handle handle = mem.read32(handles); handles += sizeof(Handle); auto object = getObject(handle); // Panic if one of the objects is not even an object if (object == nullptr) [[unlikely]] { Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle); regs[0] = Result::Kernel::InvalidHandle; return; } // Panic if one of the objects is not a valid sync object if (!isWaitable(object)) [[unlikely]] { Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n", object->getTypeName(), handle); } if (shouldWaitOnObject(object)) { allReady = false; // Derp, not all objects are ready :( } else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down if (!oneObjectReady) { oneObjectReady = true; firstReadyObjectIndex = i; } } waitObjects[i] = {handle, object}; } auto& t = threads[currentThreadIndex]; // We only need to wait on one object. Easy. if (!waitAll) { // If there's ready objects, acquire the first one and return if (oneObjectReady) { regs[0] = Result::Success; regs[1] = firstReadyObjectIndex; // Return index of the acquired object acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object return; } regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout // If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up regs[1] = 0xFFFFFFFF; t.waitList.resize(handleCount); t.status = ThreadStatus::WaitSyncAny; t.outPointer = outPointer; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); for (s32 i = 0; i < handleCount; i++) { t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist } requireReschedule(); } else { Helpers::panic("WaitSynchronizationN with waitAll"); } }