diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 163b19e1..55dfea55 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -82,6 +82,8 @@ private: bool canThreadRun(const Thread& t); bool shouldWaitOnObject(KernelObject* object); void releaseMutex(Mutex* moo); + void cancelTimer(Timer* timer); + void signalTimer(Handle timerHandle, Timer* timer); // Wake up the thread with the highest priority out of all threads in the waitlist // Returns the index of the woken up thread diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index abefbcd9..30340221 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -207,21 +207,23 @@ struct KernelObject { } // Retrieves a reference to the waitlist for a specified object - // We return a reference because this function is only called in the kernel threading internals - // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. - // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. - // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object - // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object - u64& getWaitlist() { - // This code is actually kinda trash but eh good enough - switch (type) { - case KernelObjectType::Event: return getData()->waitlist; - case KernelObjectType::Mutex: return getData()->waitlist; - case KernelObjectType::Semaphore: return getData()->waitlist; - case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; - // This should be unreachable once we fully implement sync objects - default: [[unlikely]] + // We return a reference because this function is only called in the kernel threading internals + // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. + // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. + // Each bit corresponds to a thread index and denotes whether the corresponding thread is waiting on this object + // For example if bit 0 of the wait list is set, then the thread with index 0 is waiting on our object + u64& getWaitlist() { + // This code is actually kinda trash but eh good enough + switch (type) { + case KernelObjectType::Event: return getData()->waitlist; + case KernelObjectType::Mutex: return getData()->waitlist; + case KernelObjectType::Semaphore: return getData()->waitlist; + case KernelObjectType::Thread: return getData()->threadsWaitingForTermination; + case KernelObjectType::Timer: return getData()->waitlist; + + // This should be unreachable once we fully implement sync objects + default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName()); - } - } + } + } }; \ No newline at end of file diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 08fc697d..53d7342b 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -252,6 +252,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) { case KernelObjectType::Thread: break; + case KernelObjectType::Timer: { + Timer* timer = object->getData(); + if (timer->resetType == ResetType::OneShot) { // One-shot timers automatically get cleared after waking up a thread + timer->fired = false; + } + break; + } + default: Helpers::panic("Acquiring unimplemented sync object %s", object->getTypeName()); } } diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index 6750bcc1..917fa573 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -4,20 +4,81 @@ Handle Kernel::makeTimer(ResetType type) { Handle ret = makeObject(KernelObjectType::Timer); objects[ret].data = new Timer(type); + if (type == ResetType::Pulse) Helpers::panic("Created pulse timer"); + return ret; } +void Kernel::cancelTimer(Timer* timer) { + // TODO: When we have a scheduler this should properly cancel timer events in the scheduler +} + +void Kernel::signalTimer(Handle timerHandle, Timer* timer) { + timer->fired = true; + wakeupAllThreads(timer->waitlist, timerHandle); + + switch (timer->resetType) { + case ResetType::OneShot: timer->fired = false; break; + case ResetType::Sticky: break; + case ResetType::Pulse: Helpers::panic("Signalled pulsing timer"); break; + } +} + void Kernel::svcCreateTimer() { const u32 resetType = regs[1]; if (resetType > 2) { Helpers::panic("Invalid reset type for event %d", resetType); } + // Have a warning here until our timers don't suck + Helpers::warn("Called Kernel::CreateTimer"); + logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType)); regs[0] = Result::Success; regs[1] = makeTimer(static_cast(resetType)); } -void Kernel::svcSetTimer() { Helpers::panic("Kernel::SetTimer"); } -void Kernel::svcClearTimer() { Helpers::panic("Kernel::ClearTimer"); } +void Kernel::svcSetTimer() { + Handle handle = regs[0]; + // TODO: Is this actually s64 or u64? 3DBrew says s64, but u64 makes more sense + const s64 initial = s64(u64(regs[1]) | (u64(regs[2]) << 32)); + const s64 interval = s64(u64(regs[3]) | (u64(regs[4]) << 32)); + logSVC("SetTimer (handle = %X, initial delay = %llxX, interval delay = %llx)\n", handle, initial, interval); + + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to set non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } + + Timer* timer = object->getData(); + cancelTimer(timer); + timer->initialDelay = initial; + timer->interval = interval; + + // If the initial delay is 0 then instantly signal the timer + if (initial == 0) { + signalTimer(handle, timer); + } else { + // This should schedule an event in the scheduler when we have one + } + + regs[0] = Result::Success; +} + +void Kernel::svcClearTimer() { + Handle handle = regs[0]; + logSVC("ClearTimer (handle = %X)\n", handle); + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to clear non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } else { + object->getData()->fired = false; + regs[0] = Result::Success; + } +} + void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); } \ No newline at end of file