summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp75
-rw-r--r--src/core/hle/kernel/k_hardware_timer.h50
-rw-r--r--src/core/hle/kernel/k_hardware_timer_base.h92
-rw-r--r--src/core/hle/kernel/k_thread.h16
-rw-r--r--src/core/hle/kernel/k_timer_task.h40
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
10namespace Kernel {
11
12void 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
22void KHardwareTimer::Finalize() {
23 this->DisableInterrupt();
24}
25
26void 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
53void 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
62void 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
67s64 KHardwareTimer::GetTick() {
68 return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
69}
70
71bool 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
8namespace Core::Timing {
9struct EventType;
10} // namespace Core::Timing
11
12namespace Kernel {
13
14class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
15public:
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
37private:
38 void EnableInterrupt(s64 wakeup_time);
39 void DisableInterrupt();
40 bool GetInterruptEnabled();
41 s64 GetTick();
42 void DoTask();
43
44private:
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
10namespace Kernel {
11
12class KHardwareTimerBase {
13public:
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
25protected:
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
67private:
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
81protected:
82 KernelCore& m_kernel;
83
84private:
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
114class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>, 115class 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
118private: 120private:
@@ -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
843inline 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
8namespace Kernel {
9
10class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
11public:
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
35private:
36 // Absolute time in nanoseconds
37 s64 m_time{};
38};
39
40} // namespace Kernel