diff options
| author | 2022-12-18 16:37:19 -0500 | |
|---|---|---|
| committer | 2022-12-18 16:37:19 -0500 | |
| commit | 67c0d714c5b6e93ddb00d0807147b5673c011ac6 (patch) | |
| tree | bd35994725d098c7371506d533390800d3253f3e /src | |
| parent | Merge pull request #9470 from german77/silenceIkillYou (diff) | |
| download | yuzu-67c0d714c5b6e93ddb00d0807147b5673c011ac6.tar.gz yuzu-67c0d714c5b6e93ddb00d0807147b5673c011ac6.tar.xz yuzu-67c0d714c5b6e93ddb00d0807147b5673c011ac6.zip | |
kernel: add KHardwareTimer
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer.cpp | 75 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer.h | 50 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer_base.h | 92 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 16 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_timer_task.h | 40 |
6 files changed, 271 insertions, 6 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6b5ac196..dcccd0435 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -201,6 +201,9 @@ add_library(core STATIC | |||
| 201 | hle/kernel/k_event_info.h | 201 | hle/kernel/k_event_info.h |
| 202 | hle/kernel/k_handle_table.cpp | 202 | hle/kernel/k_handle_table.cpp |
| 203 | hle/kernel/k_handle_table.h | 203 | hle/kernel/k_handle_table.h |
| 204 | hle/kernel/k_hardware_timer_base.h | ||
| 205 | hle/kernel/k_hardware_timer.cpp | ||
| 206 | hle/kernel/k_hardware_timer.h | ||
| 204 | hle/kernel/k_interrupt_manager.cpp | 207 | hle/kernel/k_interrupt_manager.cpp |
| 205 | hle/kernel/k_interrupt_manager.h | 208 | hle/kernel/k_interrupt_manager.h |
| 206 | hle/kernel/k_light_condition_variable.cpp | 209 | hle/kernel/k_light_condition_variable.cpp |
| @@ -268,6 +271,7 @@ add_library(core STATIC | |||
| 268 | hle/kernel/k_thread_local_page.h | 271 | hle/kernel/k_thread_local_page.h |
| 269 | hle/kernel/k_thread_queue.cpp | 272 | hle/kernel/k_thread_queue.cpp |
| 270 | hle/kernel/k_thread_queue.h | 273 | hle/kernel/k_thread_queue.h |
| 274 | hle/kernel/k_timer_task.h | ||
| 271 | hle/kernel/k_trace.h | 275 | hle/kernel/k_trace.h |
| 272 | hle/kernel/k_transfer_memory.cpp | 276 | hle/kernel/k_transfer_memory.cpp |
| 273 | hle/kernel/k_transfer_memory.h | 277 | hle/kernel/k_transfer_memory.h |
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp new file mode 100644 index 000000000..afa777f9a --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.cpp | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/core.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_hardware_timer.h" | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | #include "core/hle/kernel/time_manager.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | void KHardwareTimer::Initialize() { | ||
| 13 | // Create the timing callback to register with CoreTiming. | ||
| 14 | m_event_type = Core::Timing::CreateEvent( | ||
| 15 | "KHardwareTimer::Callback", | ||
| 16 | [this](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { | ||
| 17 | reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); | ||
| 18 | return std::nullopt; | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | void KHardwareTimer::Finalize() { | ||
| 23 | this->DisableInterrupt(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void KHardwareTimer::DoTask() { | ||
| 27 | // Handle the interrupt. | ||
| 28 | { | ||
| 29 | KScopedSchedulerLock slk{m_kernel}; | ||
| 30 | KScopedSpinLock lk(this->GetLock()); | ||
| 31 | |||
| 32 | //! Ignore this event if needed. | ||
| 33 | if (!this->GetInterruptEnabled()) { | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | // Disable the timer interrupt while we handle this. | ||
| 38 | this->DisableInterrupt(); | ||
| 39 | |||
| 40 | if (const s64 next_time = this->DoInterruptTaskImpl(GetTick()); | ||
| 41 | 0 < next_time && next_time <= m_wakeup_time) { | ||
| 42 | // We have a next time, so we should set the time to interrupt and turn the interrupt | ||
| 43 | // on. | ||
| 44 | this->EnableInterrupt(next_time); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | // Clear the timer interrupt. | ||
| 49 | // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer, | ||
| 50 | // GetCurrentCoreId()); | ||
| 51 | } | ||
| 52 | |||
| 53 | void KHardwareTimer::EnableInterrupt(s64 wakeup_time) { | ||
| 54 | this->DisableInterrupt(); | ||
| 55 | |||
| 56 | m_wakeup_time = wakeup_time; | ||
| 57 | m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, | ||
| 58 | m_event_type, reinterpret_cast<uintptr_t>(this), | ||
| 59 | true); | ||
| 60 | } | ||
| 61 | |||
| 62 | void KHardwareTimer::DisableInterrupt() { | ||
| 63 | m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); | ||
| 64 | m_wakeup_time = std::numeric_limits<s64>::max(); | ||
| 65 | } | ||
| 66 | |||
| 67 | s64 KHardwareTimer::GetTick() { | ||
| 68 | return m_kernel.System().CoreTiming().GetGlobalTimeNs().count(); | ||
| 69 | } | ||
| 70 | |||
| 71 | bool KHardwareTimer::GetInterruptEnabled() { | ||
| 72 | return m_wakeup_time != std::numeric_limits<s64>::max(); | ||
| 73 | } | ||
| 74 | |||
| 75 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_hardware_timer.h b/src/core/hle/kernel/k_hardware_timer.h new file mode 100644 index 000000000..2c88876b3 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/kernel/k_hardware_timer_base.h" | ||
| 7 | |||
| 8 | namespace Core::Timing { | ||
| 9 | struct EventType; | ||
| 10 | } // namespace Core::Timing | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase { | ||
| 15 | public: | ||
| 16 | explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {} | ||
| 17 | |||
| 18 | // Public API. | ||
| 19 | void Initialize(); | ||
| 20 | void Finalize(); | ||
| 21 | |||
| 22 | s64 GetCount() { | ||
| 23 | return GetTick(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) { | ||
| 27 | KScopedDisableDispatch dd{m_kernel}; | ||
| 28 | KScopedSpinLock lk{this->GetLock()}; | ||
| 29 | |||
| 30 | if (this->RegisterAbsoluteTaskImpl(task, task_time)) { | ||
| 31 | if (task_time <= m_wakeup_time) { | ||
| 32 | this->EnableInterrupt(task_time); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | private: | ||
| 38 | void EnableInterrupt(s64 wakeup_time); | ||
| 39 | void DisableInterrupt(); | ||
| 40 | bool GetInterruptEnabled(); | ||
| 41 | s64 GetTick(); | ||
| 42 | void DoTask(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | // Absolute time in nanoseconds | ||
| 46 | s64 m_wakeup_time{std::numeric_limits<s64>::max()}; | ||
| 47 | std::shared_ptr<Core::Timing::EventType> m_event_type{}; | ||
| 48 | }; | ||
| 49 | |||
| 50 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_hardware_timer_base.h b/src/core/hle/kernel/k_hardware_timer_base.h new file mode 100644 index 000000000..6318b35bd --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer_base.h | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "core/hle/kernel/k_spin_lock.h" | ||
| 7 | #include "core/hle/kernel/k_thread.h" | ||
| 8 | #include "core/hle/kernel/k_timer_task.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | class KHardwareTimerBase { | ||
| 13 | public: | ||
| 14 | explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {} | ||
| 15 | |||
| 16 | void CancelTask(KTimerTask* task) { | ||
| 17 | KScopedDisableDispatch dd{m_kernel}; | ||
| 18 | KScopedSpinLock lk{m_lock}; | ||
| 19 | |||
| 20 | if (const s64 task_time = task->GetTime(); task_time > 0) { | ||
| 21 | this->RemoveTaskFromTree(task); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | |||
| 25 | protected: | ||
| 26 | KSpinLock& GetLock() { | ||
| 27 | return m_lock; | ||
| 28 | } | ||
| 29 | |||
| 30 | s64 DoInterruptTaskImpl(s64 cur_time) { | ||
| 31 | // We want to handle all tasks, returning the next time that a task is scheduled. | ||
| 32 | while (true) { | ||
| 33 | // Get the next task. If there isn't one, return 0. | ||
| 34 | KTimerTask* task = m_next_task; | ||
| 35 | if (task == nullptr) { | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | // If the task needs to be done in the future, do it in the future and not now. | ||
| 40 | if (const s64 task_time = task->GetTime(); task_time > cur_time) { | ||
| 41 | return task_time; | ||
| 42 | } | ||
| 43 | |||
| 44 | // Remove the task from the tree of tasks, and update our next task. | ||
| 45 | this->RemoveTaskFromTree(task); | ||
| 46 | |||
| 47 | // Handle the task. | ||
| 48 | task->OnTimer(); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) { | ||
| 53 | ASSERT(task_time > 0); | ||
| 54 | |||
| 55 | // Set the task's time, and insert it into our tree. | ||
| 56 | task->SetTime(task_time); | ||
| 57 | m_task_tree.insert(*task); | ||
| 58 | |||
| 59 | // Update our next task if relevant. | ||
| 60 | if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) { | ||
| 61 | return false; | ||
| 62 | } | ||
| 63 | m_next_task = task; | ||
| 64 | return true; | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | void RemoveTaskFromTree(KTimerTask* task) { | ||
| 69 | // Erase from the tree. | ||
| 70 | auto it = m_task_tree.erase(m_task_tree.iterator_to(*task)); | ||
| 71 | |||
| 72 | // Clear the task's scheduled time. | ||
| 73 | task->SetTime(0); | ||
| 74 | |||
| 75 | // Update our next task if relevant. | ||
| 76 | if (m_next_task == task) { | ||
| 77 | m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | protected: | ||
| 82 | KernelCore& m_kernel; | ||
| 83 | |||
| 84 | private: | ||
| 85 | using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>; | ||
| 86 | |||
| 87 | KSpinLock m_lock{}; | ||
| 88 | TimerTaskTree m_task_tree{}; | ||
| 89 | KTimerTask* m_next_task{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dc52b4ed3..1320451c0 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "core/hle/kernel/k_light_lock.h" | 22 | #include "core/hle/kernel/k_light_lock.h" |
| 23 | #include "core/hle/kernel/k_spin_lock.h" | 23 | #include "core/hle/kernel/k_spin_lock.h" |
| 24 | #include "core/hle/kernel/k_synchronization_object.h" | 24 | #include "core/hle/kernel/k_synchronization_object.h" |
| 25 | #include "core/hle/kernel/k_timer_task.h" | ||
| 25 | #include "core/hle/kernel/k_worker_task.h" | 26 | #include "core/hle/kernel/k_worker_task.h" |
| 26 | #include "core/hle/kernel/slab_helpers.h" | 27 | #include "core/hle/kernel/slab_helpers.h" |
| 27 | #include "core/hle/kernel/svc_common.h" | 28 | #include "core/hle/kernel/svc_common.h" |
| @@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread); | |||
| 112 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); | 113 | [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); |
| 113 | 114 | ||
| 114 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, | 115 | class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, |
| 115 | public boost::intrusive::list_base_hook<> { | 116 | public boost::intrusive::list_base_hook<>, |
| 117 | public KTimerTask { | ||
| 116 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); | 118 | KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject); |
| 117 | 119 | ||
| 118 | private: | 120 | private: |
| @@ -660,7 +662,7 @@ private: | |||
| 660 | union SyncObjectBuffer { | 662 | union SyncObjectBuffer { |
| 661 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; | 663 | std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; |
| 662 | std::array<Handle, | 664 | std::array<Handle, |
| 663 | Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> | 665 | Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))> |
| 664 | handles; | 666 | handles; |
| 665 | constexpr SyncObjectBuffer() {} | 667 | constexpr SyncObjectBuffer() {} |
| 666 | }; | 668 | }; |
| @@ -681,10 +683,8 @@ private: | |||
| 681 | }; | 683 | }; |
| 682 | 684 | ||
| 683 | template <typename T> | 685 | template <typename T> |
| 684 | requires( | 686 | requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>) |
| 685 | std::same_as<T, KThread> || | 687 | static constexpr int Compare(const T& lhs, const KThread& rhs) { |
| 686 | std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs, | ||
| 687 | const KThread& rhs) { | ||
| 688 | const u64 l_key = lhs.GetConditionVariableKey(); | 688 | const u64 l_key = lhs.GetConditionVariableKey(); |
| 689 | const u64 r_key = rhs.GetConditionVariableKey(); | 689 | const u64 r_key = rhs.GetConditionVariableKey(); |
| 690 | 690 | ||
| @@ -840,4 +840,8 @@ private: | |||
| 840 | KernelCore& kernel; | 840 | KernelCore& kernel; |
| 841 | }; | 841 | }; |
| 842 | 842 | ||
| 843 | inline void KTimerTask::OnTimer() { | ||
| 844 | static_cast<KThread*>(this)->OnTimer(); | ||
| 845 | } | ||
| 846 | |||
| 843 | } // namespace Kernel | 847 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_timer_task.h b/src/core/hle/kernel/k_timer_task.h new file mode 100644 index 000000000..66f0a5a90 --- /dev/null +++ b/src/core/hle/kernel/k_timer_task.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/intrusive_red_black_tree.h" | ||
| 7 | |||
| 8 | namespace Kernel { | ||
| 9 | |||
| 10 | class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> { | ||
| 11 | public: | ||
| 12 | static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) { | ||
| 13 | if (lhs.GetTime() < rhs.GetTime()) { | ||
| 14 | return -1; | ||
| 15 | } else { | ||
| 16 | return 1; | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | constexpr explicit KTimerTask() = default; | ||
| 21 | |||
| 22 | constexpr void SetTime(s64 t) { | ||
| 23 | m_time = t; | ||
| 24 | } | ||
| 25 | |||
| 26 | constexpr s64 GetTime() const { | ||
| 27 | return m_time; | ||
| 28 | } | ||
| 29 | |||
| 30 | // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a | ||
| 31 | // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have | ||
| 32 | // devirtualized (see inline declaration for this inside k_thread.h). | ||
| 33 | void OnTimer(); | ||
| 34 | |||
| 35 | private: | ||
| 36 | // Absolute time in nanoseconds | ||
| 37 | s64 m_time{}; | ||
| 38 | }; | ||
| 39 | |||
| 40 | } // namespace Kernel | ||