From 5dd9a6fc26a606d1f1ea35c584b26052d47153de Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Mon, 19 Dec 2022 18:52:58 +0100 Subject: [PATCH] early-access version 3234 --- README.md | 2 +- src/core/CMakeLists.txt | 6 +- src/core/hle/kernel/k_address_arbiter.cpp | 1 - src/core/hle/kernel/k_hardware_timer.cpp | 74 +++++++++++++++ src/core/hle/kernel/k_hardware_timer.h | 54 +++++++++++ src/core/hle/kernel/k_hardware_timer_base.h | 92 +++++++++++++++++++ .../k_scoped_scheduler_lock_and_sleep.h | 4 +- src/core/hle/kernel/k_thread.h | 8 +- src/core/hle/kernel/k_thread_queue.cpp | 6 +- src/core/hle/kernel/k_timer_task.h | 40 ++++++++ src/core/hle/kernel/kernel.cpp | 20 ++-- src/core/hle/kernel/kernel.h | 9 +- 12 files changed, 291 insertions(+), 25 deletions(-) create mode 100755 src/core/hle/kernel/k_hardware_timer.cpp create mode 100755 src/core/hle/kernel/k_hardware_timer.h create mode 100755 src/core/hle/kernel/k_hardware_timer_base.h create mode 100755 src/core/hle/kernel/k_timer_task.h diff --git a/README.md b/README.md index a1a3411f0..e18168b13 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3233. +This is the source code for early-access 3234. ## Legal Notice diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0785ea496..d92cc16eb 100755 --- 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 76b592ba3..634aefc0e 100755 --- 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 100755 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(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(this), + true); +} + +void KHardwareTimer::DisableInterrupt() { + m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast(this)); + m_wakeup_time = std::numeric_limits::max(); +} + +s64 KHardwareTimer::GetTick() const { + return m_kernel.System().CoreTiming().GetGlobalTimeNs().count(); +} + +bool KHardwareTimer::GetInterruptEnabled() { + return m_wakeup_time != std::numeric_limits::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 100755 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::max()}; + std::shared_ptr 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 100755 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::TreeType; + + 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 72a78fb91..7c3c0d944 100755 --- 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 bd0b56e9e..26929b3b3 100755 --- 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, - 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(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 e45bc82d5..bf853de5c 100755 --- 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 100755 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 { +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 5e15b1d54..2fa9464e3 100755 --- 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); + hardware_timer->Initialize(); + global_object_list_container = std::make_unique(kernel); global_scheduler_context = std::make_unique(kernel); global_handle_table = std::make_unique(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 process_list; std::atomic current_process{}; std::unique_ptr global_scheduler_context; - Kernel::TimeManager time_manager; + std::unique_ptr 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 6d1868aa4..3a4e59120 100755 --- 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; @@ -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);