diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_address_arbiter.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer.cpp | 74 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer.h | 54 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_hardware_timer_base.h | 92 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread_queue.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_timer_task.h | 40 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 20 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 9 | ||||
| -rw-r--r-- | src/core/hle/kernel/time_manager.cpp | 44 | ||||
| -rw-r--r-- | src/core/hle/kernel/time_manager.h | 41 | 
13 files changed, 290 insertions, 109 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c6b5ac196..0252c8c31 100644 --- a/src/core/CMakeLists.txt +++ b/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 @@ -290,8 +294,6 @@ add_library(core STATIC      hle/kernel/svc_common.h      hle/kernel/svc_types.h      hle/kernel/svc_wrap.h -    hle/kernel/time_manager.cpp -    hle/kernel/time_manager.h      hle/result.h      hle/service/acc/acc.cpp      hle/service/acc/acc.h diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index f85b11557..a442a3b98 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -10,7 +10,6 @@  #include "core/hle/kernel/k_thread_queue.h"  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/time_manager.h"  #include "core/memory.h"  namespace Kernel { 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..6bba79ea0 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.cpp @@ -0,0 +1,74 @@ +// 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" + +namespace Kernel { + +void KHardwareTimer::Initialize() { +    // Create the timing callback to register with CoreTiming. +    m_event_type = Core::Timing::CreateEvent( +        "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { +            reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); +            return std::nullopt; +        }); +} + +void KHardwareTimer::Finalize() { +    this->DisableInterrupt(); +    m_event_type.reset(); +} + +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() const { +    return m_kernel.System().CoreTiming().GetGlobalTimeNs().count(); +} + +bool KHardwareTimer::GetInterruptEnabled() { +    return m_wakeup_time != std::numeric_limits<s64>::max(); +} + +} // 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..00bef6ea1 --- /dev/null +++ b/src/core/hle/kernel/k_hardware_timer.h @@ -0,0 +1,54 @@ +// 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() const { +        return GetTick(); +    } + +    void RegisterTask(KTimerTask* task, s64 time_from_now) { +        this->RegisterAbsoluteTask(task, GetTick() + time_from_now); +    } + +    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() const; +    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 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 @@ +// 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 diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 76c095e69..76db65a4d 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -5,9 +5,9 @@  #include "common/common_types.h"  #include "core/hle/kernel/global_scheduler_context.h" +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_thread.h"  #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/time_manager.h"  namespace Kernel { @@ -22,7 +22,7 @@ public:      ~KScopedSchedulerLockAndSleep() {          // Register the sleep.          if (timeout_tick > 0) { -            kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); +            kernel.HardwareTimer().RegisterTask(thread, timeout_tick);          }          // Unlock the scheduler. diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index dc52b4ed3..7cd94a340 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/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: @@ -840,4 +842,8 @@ private:      KernelCore& kernel;  }; +inline void KTimerTask::OnTimer() { +    static_cast<KThread*>(this)->OnTimer(); +} +  } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp index 9f4e081ba..5f1dc97eb 100644 --- a/src/core/hle/kernel/k_thread_queue.cpp +++ b/src/core/hle/kernel/k_thread_queue.cpp @@ -1,9 +1,9 @@  // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_thread_queue.h"  #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/time_manager.h"  namespace Kernel { @@ -22,7 +22,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {      waiting_thread->ClearWaitQueue();      // Cancel the thread task. -    kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); +    kernel.HardwareTimer().CancelTask(waiting_thread);  }  void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) { @@ -37,7 +37,7 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool      // Cancel the thread task.      if (cancel_timer_task) { -        kernel.TimeManager().UnscheduleTimeEvent(waiting_thread); +        kernel.HardwareTimer().CancelTask(waiting_thread);      }  } 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 @@ +// 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 diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 0eb74a422..b75bac5df 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -26,6 +26,7 @@  #include "core/hle/kernel/k_client_port.h"  #include "core/hle/kernel/k_dynamic_resource_manager.h"  #include "core/hle/kernel/k_handle_table.h" +#include "core/hle/kernel/k_hardware_timer.h"  #include "core/hle/kernel/k_memory_layout.h"  #include "core/hle/kernel/k_memory_manager.h"  #include "core/hle/kernel/k_page_buffer.h" @@ -39,7 +40,6 @@  #include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/service_thread.h" -#include "core/hle/kernel/time_manager.h"  #include "core/hle/result.h"  #include "core/hle/service/sm/sm.h"  #include "core/memory.h" @@ -55,7 +55,7 @@ struct KernelCore::Impl {      static constexpr size_t ReservedDynamicPageCount = 64;      explicit Impl(Core::System& system_, KernelCore& kernel_) -        : time_manager{system_}, service_threads_manager{1, "ServiceThreadsManager"}, +        : service_threads_manager{1, "ServiceThreadsManager"},            service_thread_barrier{2}, system{system_} {}      void SetMulticore(bool is_multi) { @@ -63,6 +63,9 @@ struct KernelCore::Impl {      }      void Initialize(KernelCore& kernel) { +        hardware_timer = std::make_unique<Kernel::KHardwareTimer>(kernel); +        hardware_timer->Initialize(); +          global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);          global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);          global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); @@ -193,6 +196,9 @@ struct KernelCore::Impl {          // Ensure that the object list container is finalized and properly shutdown.          global_object_list_container->Finalize();          global_object_list_container.reset(); + +        hardware_timer->Finalize(); +        hardware_timer.reset();      }      void CloseServices() { @@ -832,7 +838,7 @@ struct KernelCore::Impl {      std::vector<KProcess*> process_list;      std::atomic<KProcess*> current_process{};      std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; -    Kernel::TimeManager time_manager; +    std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;      Init::KSlabResourceCounts slab_resource_counts{};      KResourceLimit* system_resource_limit{}; @@ -1019,12 +1025,8 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {      return impl->schedulers[core_id].get();  } -Kernel::TimeManager& KernelCore::TimeManager() { -    return impl->time_manager; -} - -const Kernel::TimeManager& KernelCore::TimeManager() const { -    return impl->time_manager; +Kernel::KHardwareTimer& KernelCore::HardwareTimer() { +    return *impl->hardware_timer;  }  Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2e22fe0f6..8d22f8d2c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -39,6 +39,7 @@ class KDynamicPageManager;  class KEvent;  class KEventInfo;  class KHandleTable; +class KHardwareTimer;  class KLinkedListNode;  class KMemoryLayout;  class KMemoryManager; @@ -63,7 +64,6 @@ class KCodeMemory;  class PhysicalCore;  class ServiceThread;  class Synchronization; -class TimeManager;  using ServiceInterfaceFactory =      std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>; @@ -175,11 +175,8 @@ public:      /// Gets the an instance of the current physical CPU core.      const Kernel::PhysicalCore& CurrentPhysicalCore() const; -    /// Gets the an instance of the TimeManager Interface. -    Kernel::TimeManager& TimeManager(); - -    /// Gets the an instance of the TimeManager Interface. -    const Kernel::TimeManager& TimeManager() const; +    /// Gets the an instance of the hardware timer. +    Kernel::KHardwareTimer& HardwareTimer();      /// Stops execution of 'id' core, in order to reschedule a new thread.      void PrepareReschedule(std::size_t id); diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp deleted file mode 100644 index 5ee72c432..000000000 --- a/src/core/hle/kernel/time_manager.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/time_manager.h" - -namespace Kernel { - -TimeManager::TimeManager(Core::System& system_) : system{system_} { -    time_manager_event_type = Core::Timing::CreateEvent( -        "Kernel::TimeManagerCallback", -        [this](std::uintptr_t thread_handle, s64 time, -               std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { -            KThread* thread = reinterpret_cast<KThread*>(thread_handle); -            { -                KScopedSchedulerLock sl(system.Kernel()); -                thread->OnTimer(); -            } -            return std::nullopt; -        }); -} - -void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { -    std::scoped_lock lock{mutex}; -    if (nanoseconds > 0) { -        ASSERT(thread); -        ASSERT(thread->GetState() != ThreadState::Runnable); -        system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, -                                          time_manager_event_type, -                                          reinterpret_cast<uintptr_t>(thread)); -    } -} - -void TimeManager::UnscheduleTimeEvent(KThread* thread) { -    std::scoped_lock lock{mutex}; -    system.CoreTiming().UnscheduleEvent(time_manager_event_type, -                                        reinterpret_cast<uintptr_t>(thread)); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h deleted file mode 100644 index 94d16b3b4..000000000 --- a/src/core/hle/kernel/time_manager.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <mutex> - -namespace Core { -class System; -} // namespace Core - -namespace Core::Timing { -struct EventType; -} // namespace Core::Timing - -namespace Kernel { - -class KThread; - -/** - * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp - * method when the event is triggered. - */ -class TimeManager { -public: -    explicit TimeManager(Core::System& system); - -    /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' -    void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds); - -    /// Unschedule an existing time event -    void UnscheduleTimeEvent(KThread* thread); - -private: -    Core::System& system; -    std::shared_ptr<Core::Timing::EventType> time_manager_event_type; -    std::mutex mutex; -}; - -} // namespace Kernel | 
