diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 7439cabb..3d4b6917 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -83,6 +83,9 @@ private: bool canThreadRun(const Thread& t); bool shouldWaitOnObject(KernelObject* object); void releaseMutex(Mutex* moo); + void cancelTimer(Timer* timer); + void signalTimer(Handle timerHandle, Timer* timer); + void updateTimer(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 6f32bad4..53c60774 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -173,6 +173,19 @@ struct Semaphore { Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} }; +struct Timer { + u64 waitlist; // Refer to the getWaitlist function below for documentation + ResetType resetType = ResetType::OneShot; + + u64 startTick; // CPU tick the timer started + u64 currentDelay; // Number of ns until the timer fires next time + u64 interval; // Number of ns until the timer fires for the second and future times + bool fired; // Has this timer been signalled? + bool running; // Is this timer running or stopped? + + Timer(ResetType type) : resetType(type), startTick(0), currentDelay(0), interval(0), waitlist(0), fired(false), running(false) {} +}; + struct MemoryBlock { u32 addr = 0; u32 size = 0; @@ -206,21 +219,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/kernel.cpp b/src/core/kernel/kernel.cpp index 408be358..5eb3cddb 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -124,6 +124,7 @@ void Kernel::deleteObjectData(KernelObject& object) { case KernelObjectType::Session: delete object.getData(); return; case KernelObjectType::Mutex: delete object.getData(); return; case KernelObjectType::Semaphore: delete object.getData(); return; + case KernelObjectType::Timer: delete object.getData(); return; case KernelObjectType::Thread: return; case KernelObjectType::Dummy: return; default: [[unlikely]] Helpers::warn("unknown object type"); return; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index c05f5490..b9bbb9ad 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()); } } @@ -652,6 +660,9 @@ bool Kernel::shouldWaitOnObject(KernelObject* object) { case KernelObjectType::Thread: // Waiting on a thread waits until it's dead. If it's dead then no need to wait return object->getData()->status != ThreadStatus::Dead; + case KernelObjectType::Timer: // We should wait on a timer only if it has not been signalled + return !object->getData()->fired; + case KernelObjectType::Semaphore: // Wait if the semaphore count <= 0 return object->getData()->availableCount <= 0; diff --git a/src/core/kernel/timers.cpp b/src/core/kernel/timers.cpp index f4a89407..1c18d9de 100644 --- a/src/core/kernel/timers.cpp +++ b/src/core/kernel/timers.cpp @@ -1,6 +1,127 @@ #include "kernel.hpp" +#include "cpu.hpp" -void Kernel::svcCreateTimer() { Helpers::panic("Kernel::CreateTimer"); } -void Kernel::svcSetTimer() { Helpers::panic("Kernel::SetTimer"); } -void Kernel::svcClearTimer() { Helpers::panic("Kernel::ClearTimer"); } -void Kernel::svcCancelTimer() { Helpers::panic("Kernel::CancelTimer"); } \ No newline at end of file +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"); + } + + // timerHandles.push_back(ret); + return ret; +} + +void Kernel::updateTimer(Handle handle, Timer* timer) { + if (timer->running) { + const u64 currentTicks = cpu.getTicks(); + u64 elapsedTicks = currentTicks - timer->startTick; + + constexpr double ticksPerSec = double(CPU::ticksPerSec); + constexpr double nsPerTick = ticksPerSec / 1000000000.0; + const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick); + + // Timer has fired + if (elapsedNs >= timer->currentDelay) { + timer->startTick = currentTicks; + timer->currentDelay = timer->interval; + signalTimer(handle, timer); + } + } +} + +void Kernel::cancelTimer(Timer* timer) { + timer->running = false; + // 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; + requireReschedule(); + + // Check if there's any thread waiting on this event + if (timer->waitlist != 0) { + wakeupAllThreads(timer->waitlist, timerHandle); + timer->waitlist = 0; // No threads waiting; + + 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. Timers are currently not updated nor triggered properly!"); + + logSVC("CreateTimer (resetType = %s)\n", resetTypeToString(resetType)); + regs[0] = Result::Success; + regs[1] = makeTimer(static_cast(resetType)); +} + +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 = %llX, 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->currentDelay = initial; + timer->interval = interval; + timer->running = true; + timer->startTick = cpu.getTicks(); + + // 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() { + Handle handle = regs[0]; + logSVC("CancelTimer (handle = %X)\n", handle); + KernelObject* object = getObject(handle, KernelObjectType::Timer); + + if (object == nullptr) { + Helpers::panic("Tried to cancel non-existent timer %X\n", handle); + regs[0] = Result::Kernel::InvalidHandle; + } else { + cancelTimer(object->getData()); + regs[0] = Result::Success; + } +} \ No newline at end of file