Liam 3 лет назад
Родитель
Сommit
67c0d714c5

+ 4 - 0
src/core/CMakeLists.txt

@@ -201,6 +201,9 @@ add_library(core STATIC
     hle/kernel/k_event_info.h
     hle/kernel/k_handle_table.cpp
     hle/kernel/k_handle_table.h
+    hle/kernel/k_hardware_timer_base.h
+    hle/kernel/k_hardware_timer.cpp
+    hle/kernel/k_hardware_timer.h
     hle/kernel/k_interrupt_manager.cpp
     hle/kernel/k_interrupt_manager.h
     hle/kernel/k_light_condition_variable.cpp
@@ -268,6 +271,7 @@ add_library(core STATIC
     hle/kernel/k_thread_local_page.h
     hle/kernel/k_thread_queue.cpp
     hle/kernel/k_thread_queue.h
+    hle/kernel/k_timer_task.h
     hle/kernel/k_trace.h
     hle/kernel/k_transfer_memory.cpp
     hle/kernel/k_transfer_memory.h

+ 75 - 0
src/core/hle/kernel/k_hardware_timer.cpp

@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/k_hardware_timer.h"
+#include "core/hle/kernel/k_scheduler.h"
+#include "core/hle/kernel/time_manager.h"
+
+namespace Kernel {
+
+void KHardwareTimer::Initialize() {
+    // Create the timing callback to register with CoreTiming.
+    m_event_type = Core::Timing::CreateEvent(
+        "KHardwareTimer::Callback",
+        [this](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) {
+            reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask();
+            return std::nullopt;
+        });
+}
+
+void KHardwareTimer::Finalize() {
+    this->DisableInterrupt();
+}
+
+void KHardwareTimer::DoTask() {
+    // Handle the interrupt.
+    {
+        KScopedSchedulerLock slk{m_kernel};
+        KScopedSpinLock lk(this->GetLock());
+
+        //! Ignore this event if needed.
+        if (!this->GetInterruptEnabled()) {
+            return;
+        }
+
+        // Disable the timer interrupt while we handle this.
+        this->DisableInterrupt();
+
+        if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
+            0 < next_time && next_time <= m_wakeup_time) {
+            // We have a next time, so we should set the time to interrupt and turn the interrupt
+            // on.
+            this->EnableInterrupt(next_time);
+        }
+    }
+
+    // Clear the timer interrupt.
+    // Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_NonSecurePhysicalTimer,
+    //                                              GetCurrentCoreId());
+}
+
+void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
+    this->DisableInterrupt();
+
+    m_wakeup_time = wakeup_time;
+    m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
+                                                 m_event_type, reinterpret_cast<uintptr_t>(this),
+                                                 true);
+}
+
+void KHardwareTimer::DisableInterrupt() {
+    m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this));
+    m_wakeup_time = std::numeric_limits<s64>::max();
+}
+
+s64 KHardwareTimer::GetTick() {
+    return m_kernel.System().CoreTiming().GetGlobalTimeNs().count();
+}
+
+bool KHardwareTimer::GetInterruptEnabled() {
+    return m_wakeup_time != std::numeric_limits<s64>::max();
+}
+
+} // namespace Kernel

+ 50 - 0
src/core/hle/kernel/k_hardware_timer.h

@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_hardware_timer_base.h"
+
+namespace Core::Timing {
+struct EventType;
+} // namespace Core::Timing
+
+namespace Kernel {
+
+class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
+public:
+    explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
+
+    // Public API.
+    void Initialize();
+    void Finalize();
+
+    s64 GetCount() {
+        return GetTick();
+    }
+
+    void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
+        KScopedDisableDispatch dd{m_kernel};
+        KScopedSpinLock lk{this->GetLock()};
+
+        if (this->RegisterAbsoluteTaskImpl(task, task_time)) {
+            if (task_time <= m_wakeup_time) {
+                this->EnableInterrupt(task_time);
+            }
+        }
+    }
+
+private:
+    void EnableInterrupt(s64 wakeup_time);
+    void DisableInterrupt();
+    bool GetInterruptEnabled();
+    s64 GetTick();
+    void DoTask();
+
+private:
+    // Absolute time in nanoseconds
+    s64 m_wakeup_time{std::numeric_limits<s64>::max()};
+    std::shared_ptr<Core::Timing::EventType> m_event_type{};
+};
+
+} // namespace Kernel

+ 92 - 0
src/core/hle/kernel/k_hardware_timer_base.h

@@ -0,0 +1,92 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_timer_task.h"
+
+namespace Kernel {
+
+class KHardwareTimerBase {
+public:
+    explicit KHardwareTimerBase(KernelCore& kernel) : m_kernel{kernel} {}
+
+    void CancelTask(KTimerTask* task) {
+        KScopedDisableDispatch dd{m_kernel};
+        KScopedSpinLock lk{m_lock};
+
+        if (const s64 task_time = task->GetTime(); task_time > 0) {
+            this->RemoveTaskFromTree(task);
+        }
+    }
+
+protected:
+    KSpinLock& GetLock() {
+        return m_lock;
+    }
+
+    s64 DoInterruptTaskImpl(s64 cur_time) {
+        // We want to handle all tasks, returning the next time that a task is scheduled.
+        while (true) {
+            // Get the next task. If there isn't one, return 0.
+            KTimerTask* task = m_next_task;
+            if (task == nullptr) {
+                return 0;
+            }
+
+            // If the task needs to be done in the future, do it in the future and not now.
+            if (const s64 task_time = task->GetTime(); task_time > cur_time) {
+                return task_time;
+            }
+
+            // Remove the task from the tree of tasks, and update our next task.
+            this->RemoveTaskFromTree(task);
+
+            // Handle the task.
+            task->OnTimer();
+        }
+    }
+
+    bool RegisterAbsoluteTaskImpl(KTimerTask* task, s64 task_time) {
+        ASSERT(task_time > 0);
+
+        // Set the task's time, and insert it into our tree.
+        task->SetTime(task_time);
+        m_task_tree.insert(*task);
+
+        // Update our next task if relevant.
+        if (m_next_task != nullptr && m_next_task->GetTime() <= task_time) {
+            return false;
+        }
+        m_next_task = task;
+        return true;
+    }
+
+private:
+    void RemoveTaskFromTree(KTimerTask* task) {
+        // Erase from the tree.
+        auto it = m_task_tree.erase(m_task_tree.iterator_to(*task));
+
+        // Clear the task's scheduled time.
+        task->SetTime(0);
+
+        // Update our next task if relevant.
+        if (m_next_task == task) {
+            m_next_task = (it != m_task_tree.end()) ? std::addressof(*it) : nullptr;
+        }
+    }
+
+protected:
+    KernelCore& m_kernel;
+
+private:
+    using TimerTaskTree = Common::IntrusiveRedBlackTreeBaseTraits<KTimerTask>::TreeType<KTimerTask>;
+
+    KSpinLock m_lock{};
+    TimerTaskTree m_task_tree{};
+    KTimerTask* m_next_task{};
+};
+
+} // namespace Kernel

+ 10 - 6
src/core/hle/kernel/k_thread.h

@@ -22,6 +22,7 @@
 #include "core/hle/kernel/k_light_lock.h"
 #include "core/hle/kernel/k_spin_lock.h"
 #include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_timer_task.h"
 #include "core/hle/kernel/k_worker_task.h"
 #include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/kernel/svc_common.h"
@@ -112,7 +113,8 @@ void SetCurrentThread(KernelCore& kernel, KThread* thread);
 [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
 
 class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
-                      public boost::intrusive::list_base_hook<> {
+                      public boost::intrusive::list_base_hook<>,
+                      public KTimerTask {
     KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
 
 private:
@@ -660,7 +662,7 @@ private:
     union SyncObjectBuffer {
         std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{};
         std::array<Handle,
-                   Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))>
+                   Svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject*) / sizeof(Handle))>
             handles;
         constexpr SyncObjectBuffer() {}
     };
@@ -681,10 +683,8 @@ private:
         };
 
         template <typename T>
-        requires(
-            std::same_as<T, KThread> ||
-            std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
-                                                                           const KThread& rhs) {
+            requires(std::same_as<T, KThread> || std::same_as<T, RedBlackKeyType>)
+        static constexpr int Compare(const T& lhs, const KThread& rhs) {
             const u64 l_key = lhs.GetConditionVariableKey();
             const u64 r_key = rhs.GetConditionVariableKey();
 
@@ -840,4 +840,8 @@ private:
     KernelCore& kernel;
 };
 
+inline void KTimerTask::OnTimer() {
+    static_cast<KThread*>(this)->OnTimer();
+}
+
 } // namespace Kernel

+ 40 - 0
src/core/hle/kernel/k_timer_task.h

@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/intrusive_red_black_tree.h"
+
+namespace Kernel {
+
+class KTimerTask : public Common::IntrusiveRedBlackTreeBaseNode<KTimerTask> {
+public:
+    static constexpr int Compare(const KTimerTask& lhs, const KTimerTask& rhs) {
+        if (lhs.GetTime() < rhs.GetTime()) {
+            return -1;
+        } else {
+            return 1;
+        }
+    }
+
+    constexpr explicit KTimerTask() = default;
+
+    constexpr void SetTime(s64 t) {
+        m_time = t;
+    }
+
+    constexpr s64 GetTime() const {
+        return m_time;
+    }
+
+    // NOTE: This is virtual in Nintendo's kernel. Prior to 13.0.0, KWaitObject was also a
+    // TimerTask; this is no longer the case. Since this is now KThread exclusive, we have
+    // devirtualized (see inline declaration for this inside k_thread.h).
+    void OnTimer();
+
+private:
+    // Absolute time in nanoseconds
+    s64 m_time{};
+};
+
+} // namespace Kernel