diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/global_scheduler_context.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_interrupt_manager.cpp | 34 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_interrupt_manager.h | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_process.cpp | 12 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_process.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scheduler.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 44 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 21 |
10 files changed, 140 insertions, 14 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 698c4f912..b1a746727 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -187,6 +187,8 @@ add_library(core STATIC | |||
| 187 | hle/kernel/k_event.h | 187 | hle/kernel/k_event.h |
| 188 | hle/kernel/k_handle_table.cpp | 188 | hle/kernel/k_handle_table.cpp |
| 189 | hle/kernel/k_handle_table.h | 189 | hle/kernel/k_handle_table.h |
| 190 | hle/kernel/k_interrupt_manager.cpp | ||
| 191 | hle/kernel/k_interrupt_manager.h | ||
| 190 | hle/kernel/k_light_condition_variable.cpp | 192 | hle/kernel/k_light_condition_variable.cpp |
| 191 | hle/kernel/k_light_condition_variable.h | 193 | hle/kernel/k_light_condition_variable.h |
| 192 | hle/kernel/k_light_lock.cpp | 194 | hle/kernel/k_light_lock.cpp |
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index 4f4e338e3..baad2c5d6 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/global_scheduler_context.h" | 9 | #include "core/hle/kernel/global_scheduler_context.h" |
| 10 | #include "core/hle/kernel/k_scheduler.h" | 10 | #include "core/hle/kernel/k_scheduler.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/physical_core.h" | ||
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| @@ -42,6 +43,11 @@ void GlobalSchedulerContext::PreemptThreads() { | |||
| 42 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | 43 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 43 | const u32 priority = preemption_priorities[core_id]; | 44 | const u32 priority = preemption_priorities[core_id]; |
| 44 | kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); | 45 | kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); |
| 46 | |||
| 47 | // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result | ||
| 48 | // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system | ||
| 49 | // interrupts that may have occurred. | ||
| 50 | kernel.PhysicalCore(core_id).Interrupt(); | ||
| 45 | } | 51 | } |
| 46 | } | 52 | } |
| 47 | 53 | ||
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp new file mode 100644 index 000000000..e5dd39751 --- /dev/null +++ b/src/core/hle/kernel/k_interrupt_manager.cpp | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/kernel/k_interrupt_manager.h" | ||
| 6 | #include "core/hle/kernel/k_process.h" | ||
| 7 | #include "core/hle/kernel/k_scheduler.h" | ||
| 8 | #include "core/hle/kernel/k_thread.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | ||
| 10 | |||
| 11 | namespace Kernel::KInterruptManager { | ||
| 12 | |||
| 13 | void HandleInterrupt(KernelCore& kernel, s32 core_id) { | ||
| 14 | auto* process = kernel.CurrentProcess(); | ||
| 15 | if (!process) { | ||
| 16 | return; | ||
| 17 | } | ||
| 18 | |||
| 19 | auto& scheduler = kernel.Scheduler(core_id); | ||
| 20 | auto& current_thread = *scheduler.GetCurrentThread(); | ||
| 21 | |||
| 22 | // If the user disable count is set, we may need to pin the current thread. | ||
| 23 | if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) { | ||
| 24 | KScopedSchedulerLock sl{kernel}; | ||
| 25 | |||
| 26 | // Pin the current thread. | ||
| 27 | process->PinCurrentThread(core_id); | ||
| 28 | |||
| 29 | // Set the interrupt flag for the thread. | ||
| 30 | scheduler.GetCurrentThread()->SetInterruptFlag(); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | } // namespace Kernel::KInterruptManager | ||
diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h new file mode 100644 index 000000000..05924801e --- /dev/null +++ b/src/core/hle/kernel/k_interrupt_manager.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace Kernel { | ||
| 10 | |||
| 11 | class KernelCore; | ||
| 12 | |||
| 13 | namespace KInterruptManager { | ||
| 14 | void HandleInterrupt(KernelCore& kernel, s32 core_id); | ||
| 15 | } | ||
| 16 | |||
| 17 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 73f8bc4fe..bf98a51e2 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -220,30 +220,28 @@ bool KProcess::ReleaseUserException(KThread* thread) { | |||
| 220 | } | 220 | } |
| 221 | } | 221 | } |
| 222 | 222 | ||
| 223 | void KProcess::PinCurrentThread() { | 223 | void KProcess::PinCurrentThread(s32 core_id) { |
| 224 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 224 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 225 | 225 | ||
| 226 | // Get the current thread. | 226 | // Get the current thread. |
| 227 | const s32 core_id = GetCurrentCoreId(kernel); | 227 | KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); |
| 228 | KThread* cur_thread = GetCurrentThreadPointer(kernel); | ||
| 229 | 228 | ||
| 230 | // If the thread isn't terminated, pin it. | 229 | // If the thread isn't terminated, pin it. |
| 231 | if (!cur_thread->IsTerminationRequested()) { | 230 | if (!cur_thread->IsTerminationRequested()) { |
| 232 | // Pin it. | 231 | // Pin it. |
| 233 | PinThread(core_id, cur_thread); | 232 | PinThread(core_id, cur_thread); |
| 234 | cur_thread->Pin(); | 233 | cur_thread->Pin(core_id); |
| 235 | 234 | ||
| 236 | // An update is needed. | 235 | // An update is needed. |
| 237 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 236 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 238 | } | 237 | } |
| 239 | } | 238 | } |
| 240 | 239 | ||
| 241 | void KProcess::UnpinCurrentThread() { | 240 | void KProcess::UnpinCurrentThread(s32 core_id) { |
| 242 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 241 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 243 | 242 | ||
| 244 | // Get the current thread. | 243 | // Get the current thread. |
| 245 | const s32 core_id = GetCurrentCoreId(kernel); | 244 | KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread(); |
| 246 | KThread* cur_thread = GetCurrentThreadPointer(kernel); | ||
| 247 | 245 | ||
| 248 | // Unpin it. | 246 | // Unpin it. |
| 249 | cur_thread->Unpin(); | 247 | cur_thread->Unpin(); |
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index cb93c7e24..e7c8b5838 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h | |||
| @@ -345,8 +345,8 @@ public: | |||
| 345 | 345 | ||
| 346 | bool IsSignaled() const override; | 346 | bool IsSignaled() const override; |
| 347 | 347 | ||
| 348 | void PinCurrentThread(); | 348 | void PinCurrentThread(s32 core_id); |
| 349 | void UnpinCurrentThread(); | 349 | void UnpinCurrentThread(s32 core_id); |
| 350 | void UnpinThread(KThread* thread); | 350 | void UnpinThread(KThread* thread); |
| 351 | 351 | ||
| 352 | KLightLock& GetStateLock() { | 352 | KLightLock& GetStateLock() { |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 277201de4..31cec990e 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/core.h" | 15 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/cpu_manager.h" | 17 | #include "core/cpu_manager.h" |
| 18 | #include "core/hle/kernel/k_interrupt_manager.h" | ||
| 18 | #include "core/hle/kernel/k_process.h" | 19 | #include "core/hle/kernel/k_process.h" |
| 19 | #include "core/hle/kernel/k_scheduler.h" | 20 | #include "core/hle/kernel/k_scheduler.h" |
| 20 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | 21 | #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" |
| @@ -53,6 +54,13 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul | |||
| 53 | } | 54 | } |
| 54 | cores_pending_reschedule &= ~(1ULL << core); | 55 | cores_pending_reschedule &= ~(1ULL << core); |
| 55 | } | 56 | } |
| 57 | |||
| 58 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) { | ||
| 59 | if (kernel.PhysicalCore(core_id).IsInterrupted()) { | ||
| 60 | KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id)); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 56 | if (must_context_switch) { | 64 | if (must_context_switch) { |
| 57 | auto core_scheduler = kernel.CurrentScheduler(); | 65 | auto core_scheduler = kernel.CurrentScheduler(); |
| 58 | kernel.ExitSVCProfile(); | 66 | kernel.ExitSVCProfile(); |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index b8c993748..71e029a3f 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <atomic> | ||
| 6 | #include <cinttypes> | 7 | #include <cinttypes> |
| 7 | #include <optional> | 8 | #include <optional> |
| 8 | #include <vector> | 9 | #include <vector> |
| @@ -33,6 +34,7 @@ | |||
| 33 | #include "core/hle/kernel/svc_results.h" | 34 | #include "core/hle/kernel/svc_results.h" |
| 34 | #include "core/hle/kernel/time_manager.h" | 35 | #include "core/hle/kernel/time_manager.h" |
| 35 | #include "core/hle/result.h" | 36 | #include "core/hle/result.h" |
| 37 | #include "core/memory.h" | ||
| 36 | 38 | ||
| 37 | #ifdef ARCHITECTURE_x86_64 | 39 | #ifdef ARCHITECTURE_x86_64 |
| 38 | #include "core/arm/dynarmic/arm_dynarmic_32.h" | 40 | #include "core/arm/dynarmic/arm_dynarmic_32.h" |
| @@ -63,6 +65,13 @@ namespace Kernel { | |||
| 63 | 65 | ||
| 64 | namespace { | 66 | namespace { |
| 65 | 67 | ||
| 68 | struct ThreadLocalRegion { | ||
| 69 | static constexpr std::size_t MessageBufferSize = 0x100; | ||
| 70 | std::array<u32, MessageBufferSize / sizeof(u32)> message_buffer; | ||
| 71 | std::atomic_uint16_t disable_count; | ||
| 72 | std::atomic_uint16_t interrupt_flag; | ||
| 73 | }; | ||
| 74 | |||
| 66 | class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { | 75 | class ThreadQueueImplForKThreadSleep final : public KThreadQueueWithoutEndWait { |
| 67 | public: | 76 | public: |
| 68 | explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_) | 77 | explicit ThreadQueueImplForKThreadSleep(KernelCore& kernel_) |
| @@ -346,7 +355,7 @@ void KThread::StartTermination() { | |||
| 346 | if (parent != nullptr) { | 355 | if (parent != nullptr) { |
| 347 | parent->ReleaseUserException(this); | 356 | parent->ReleaseUserException(this); |
| 348 | if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { | 357 | if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { |
| 349 | parent->UnpinCurrentThread(); | 358 | parent->UnpinCurrentThread(core_id); |
| 350 | } | 359 | } |
| 351 | } | 360 | } |
| 352 | 361 | ||
| @@ -372,7 +381,7 @@ void KThread::StartTermination() { | |||
| 372 | this->Close(); | 381 | this->Close(); |
| 373 | } | 382 | } |
| 374 | 383 | ||
| 375 | void KThread::Pin() { | 384 | void KThread::Pin(s32 current_core) { |
| 376 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 385 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 377 | 386 | ||
| 378 | // Set ourselves as pinned. | 387 | // Set ourselves as pinned. |
| @@ -389,7 +398,6 @@ void KThread::Pin() { | |||
| 389 | 398 | ||
| 390 | // Bind ourselves to this core. | 399 | // Bind ourselves to this core. |
| 391 | const s32 active_core = GetActiveCore(); | 400 | const s32 active_core = GetActiveCore(); |
| 392 | const s32 current_core = GetCurrentCoreId(kernel); | ||
| 393 | 401 | ||
| 394 | SetActiveCore(current_core); | 402 | SetActiveCore(current_core); |
| 395 | physical_ideal_core_id = current_core; | 403 | physical_ideal_core_id = current_core; |
| @@ -482,6 +490,36 @@ void KThread::Unpin() { | |||
| 482 | } | 490 | } |
| 483 | } | 491 | } |
| 484 | 492 | ||
| 493 | u16 KThread::GetUserDisableCount() const { | ||
| 494 | if (!IsUserThread()) { | ||
| 495 | // We only emulate TLS for user threads | ||
| 496 | return {}; | ||
| 497 | } | ||
| 498 | |||
| 499 | auto& memory = kernel.System().Memory(); | ||
| 500 | return memory.Read16(tls_address + offsetof(ThreadLocalRegion, disable_count)); | ||
| 501 | } | ||
| 502 | |||
| 503 | void KThread::SetInterruptFlag() { | ||
| 504 | if (!IsUserThread()) { | ||
| 505 | // We only emulate TLS for user threads | ||
| 506 | return; | ||
| 507 | } | ||
| 508 | |||
| 509 | auto& memory = kernel.System().Memory(); | ||
| 510 | memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 1); | ||
| 511 | } | ||
| 512 | |||
| 513 | void KThread::ClearInterruptFlag() { | ||
| 514 | if (!IsUserThread()) { | ||
| 515 | // We only emulate TLS for user threads | ||
| 516 | return; | ||
| 517 | } | ||
| 518 | |||
| 519 | auto& memory = kernel.System().Memory(); | ||
| 520 | memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0); | ||
| 521 | } | ||
| 522 | |||
| 485 | ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { | 523 | ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { |
| 486 | KScopedSchedulerLock sl{kernel}; | 524 | KScopedSchedulerLock sl{kernel}; |
| 487 | 525 | ||
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index c8a08bd71..83dfde69b 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -307,6 +307,10 @@ public: | |||
| 307 | return parent != nullptr; | 307 | return parent != nullptr; |
| 308 | } | 308 | } |
| 309 | 309 | ||
| 310 | u16 GetUserDisableCount() const; | ||
| 311 | void SetInterruptFlag(); | ||
| 312 | void ClearInterruptFlag(); | ||
| 313 | |||
| 310 | [[nodiscard]] KThread* GetLockOwner() const { | 314 | [[nodiscard]] KThread* GetLockOwner() const { |
| 311 | return lock_owner; | 315 | return lock_owner; |
| 312 | } | 316 | } |
| @@ -490,7 +494,7 @@ public: | |||
| 490 | this->GetStackParameters().disable_count--; | 494 | this->GetStackParameters().disable_count--; |
| 491 | } | 495 | } |
| 492 | 496 | ||
| 493 | void Pin(); | 497 | void Pin(s32 current_core); |
| 494 | 498 | ||
| 495 | void Unpin(); | 499 | void Unpin(); |
| 496 | 500 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 63e2dff19..250ef9042 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -2027,6 +2027,25 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::Sign | |||
| 2027 | count); | 2027 | count); |
| 2028 | } | 2028 | } |
| 2029 | 2029 | ||
| 2030 | static void SynchronizePreemptionState(Core::System& system) { | ||
| 2031 | auto& kernel = system.Kernel(); | ||
| 2032 | |||
| 2033 | // Lock the scheduler. | ||
| 2034 | KScopedSchedulerLock sl{kernel}; | ||
| 2035 | |||
| 2036 | // If the current thread is pinned, unpin it. | ||
| 2037 | KProcess* cur_process = system.Kernel().CurrentProcess(); | ||
| 2038 | const auto core_id = GetCurrentCoreId(kernel); | ||
| 2039 | |||
| 2040 | if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { | ||
| 2041 | // Clear the current thread's interrupt flag. | ||
| 2042 | GetCurrentThread(kernel).ClearInterruptFlag(); | ||
| 2043 | |||
| 2044 | // Unpin the current thread. | ||
| 2045 | cur_process->UnpinCurrentThread(core_id); | ||
| 2046 | } | ||
| 2047 | } | ||
| 2048 | |||
| 2030 | static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, | 2049 | static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, |
| 2031 | s32 value, s32 count) { | 2050 | s32 value, s32 count) { |
| 2032 | return SignalToAddress(system, address, signal_type, value, count); | 2051 | return SignalToAddress(system, address, signal_type, value, count); |
| @@ -2797,7 +2816,7 @@ static const FunctionDef SVC_Table_64[] = { | |||
| 2797 | {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"}, | 2816 | {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"}, |
| 2798 | {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, | 2817 | {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, |
| 2799 | {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, | 2818 | {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, |
| 2800 | {0x36, nullptr, "SynchronizePreemptionState"}, | 2819 | {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, |
| 2801 | {0x37, nullptr, "Unknown"}, | 2820 | {0x37, nullptr, "Unknown"}, |
| 2802 | {0x38, nullptr, "Unknown"}, | 2821 | {0x38, nullptr, "Unknown"}, |
| 2803 | {0x39, nullptr, "Unknown"}, | 2822 | {0x39, nullptr, "Unknown"}, |