svc_thread.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/scope_exit.h"
  4. #include "core/core.h"
  5. #include "core/core_timing.h"
  6. #include "core/hle/kernel/k_process.h"
  7. #include "core/hle/kernel/k_scoped_resource_reservation.h"
  8. #include "core/hle/kernel/k_thread.h"
  9. #include "core/hle/kernel/svc.h"
  10. namespace Kernel::Svc {
  11. namespace {
  12. constexpr bool IsValidVirtualCoreId(int32_t core_id) {
  13. return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
  14. }
  15. } // Anonymous namespace
  16. /// Creates a new thread
  17. Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
  18. VAddr stack_bottom, u32 priority, s32 core_id) {
  19. LOG_DEBUG(Kernel_SVC,
  20. "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
  21. "priority=0x{:08X}, core_id=0x{:08X}",
  22. entry_point, arg, stack_bottom, priority, core_id);
  23. // Adjust core id, if it's the default magic.
  24. auto& kernel = system.Kernel();
  25. auto& process = *kernel.CurrentProcess();
  26. if (core_id == IdealCoreUseProcessValue) {
  27. core_id = process.GetIdealCoreId();
  28. }
  29. // Validate arguments.
  30. if (!IsValidVirtualCoreId(core_id)) {
  31. LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
  32. return ResultInvalidCoreId;
  33. }
  34. if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
  35. LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
  36. return ResultInvalidCoreId;
  37. }
  38. if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
  39. LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
  40. return ResultInvalidPriority;
  41. }
  42. if (!process.CheckThreadPriority(priority)) {
  43. LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
  44. return ResultInvalidPriority;
  45. }
  46. // Reserve a new thread from the process resource limit (waiting up to 100ms).
  47. KScopedResourceReservation thread_reservation(
  48. kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
  49. system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
  50. if (!thread_reservation.Succeeded()) {
  51. LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
  52. return ResultLimitReached;
  53. }
  54. // Create the thread.
  55. KThread* thread = KThread::Create(kernel);
  56. if (!thread) {
  57. LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
  58. return ResultOutOfResource;
  59. }
  60. SCOPE_EXIT({ thread->Close(); });
  61. // Initialize the thread.
  62. {
  63. KScopedLightLock lk{process.GetStateLock()};
  64. R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
  65. priority, core_id, &process));
  66. }
  67. // Set the thread name for debugging purposes.
  68. thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
  69. // Commit the thread reservation.
  70. thread_reservation.Commit();
  71. // Register the new thread.
  72. KThread::Register(kernel, thread);
  73. // Add the thread to the handle table.
  74. R_TRY(process.GetHandleTable().Add(out_handle, thread));
  75. return ResultSuccess;
  76. }
  77. Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point,
  78. u32 arg, u32 stack_top, s32 processor_id) {
  79. return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
  80. }
  81. /// Starts the thread for the provided handle
  82. Result StartThread(Core::System& system, Handle thread_handle) {
  83. LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
  84. // Get the thread from its handle.
  85. KScopedAutoObject thread =
  86. system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
  87. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  88. // Try to start the thread.
  89. R_TRY(thread->Run());
  90. // If we succeeded, persist a reference to the thread.
  91. thread->Open();
  92. system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
  93. return ResultSuccess;
  94. }
  95. Result StartThread32(Core::System& system, Handle thread_handle) {
  96. return StartThread(system, thread_handle);
  97. }
  98. /// Called when a thread exits
  99. void ExitThread(Core::System& system) {
  100. LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
  101. auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
  102. system.GlobalSchedulerContext().RemoveThread(current_thread);
  103. current_thread->Exit();
  104. system.Kernel().UnregisterInUseObject(current_thread);
  105. }
  106. void ExitThread32(Core::System& system) {
  107. ExitThread(system);
  108. }
  109. /// Sleep the current thread
  110. void SleepThread(Core::System& system, s64 nanoseconds) {
  111. auto& kernel = system.Kernel();
  112. const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
  113. LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
  114. // When the input tick is positive, sleep.
  115. if (nanoseconds > 0) {
  116. // Convert the timeout from nanoseconds to ticks.
  117. // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
  118. // Sleep.
  119. // NOTE: Nintendo does not check the result of this sleep.
  120. static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
  121. } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
  122. KScheduler::YieldWithoutCoreMigration(kernel);
  123. } else if (yield_type == Svc::YieldType::WithCoreMigration) {
  124. KScheduler::YieldWithCoreMigration(kernel);
  125. } else if (yield_type == Svc::YieldType::ToAnyThread) {
  126. KScheduler::YieldToAnyThread(kernel);
  127. } else {
  128. // Nintendo does nothing at all if an otherwise invalid value is passed.
  129. ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
  130. }
  131. }
  132. void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
  133. const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
  134. SleepThread(system, nanoseconds);
  135. }
  136. /// Gets the thread context
  137. Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
  138. LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
  139. thread_handle);
  140. auto& kernel = system.Kernel();
  141. // Get the thread from its handle.
  142. KScopedAutoObject thread =
  143. kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
  144. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  145. // Require the handle be to a non-current thread in the current process.
  146. const auto* current_process = kernel.CurrentProcess();
  147. R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
  148. // Verify that the thread isn't terminated.
  149. R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
  150. /// Check that the thread is not the current one.
  151. /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
  152. R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
  153. // Try to get the thread context until the thread isn't current on any core.
  154. while (true) {
  155. KScopedSchedulerLock sl{kernel};
  156. // TODO(bunnei): Enforce that thread is suspended for debug here.
  157. // If the thread's raw state isn't runnable, check if it's current on some core.
  158. if (thread->GetRawState() != ThreadState::Runnable) {
  159. bool current = false;
  160. for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
  161. if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
  162. current = true;
  163. break;
  164. }
  165. }
  166. // If the thread is current, retry until it isn't.
  167. if (current) {
  168. continue;
  169. }
  170. }
  171. // Get the thread context.
  172. std::vector<u8> context;
  173. R_TRY(thread->GetThreadContext3(context));
  174. // Copy the thread context to user space.
  175. system.Memory().WriteBlock(out_context, context.data(), context.size());
  176. return ResultSuccess;
  177. }
  178. return ResultSuccess;
  179. }
  180. Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
  181. return GetThreadContext(system, out_context, thread_handle);
  182. }
  183. /// Gets the priority for the specified thread
  184. Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
  185. LOG_TRACE(Kernel_SVC, "called");
  186. // Get the thread from its handle.
  187. KScopedAutoObject thread =
  188. system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
  189. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  190. // Get the thread's priority.
  191. *out_priority = thread->GetPriority();
  192. return ResultSuccess;
  193. }
  194. Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
  195. return GetThreadPriority(system, out_priority, handle);
  196. }
  197. /// Sets the priority for the specified thread
  198. Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
  199. // Get the current process.
  200. KProcess& process = *system.Kernel().CurrentProcess();
  201. // Validate the priority.
  202. R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
  203. ResultInvalidPriority);
  204. R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
  205. // Get the thread from its handle.
  206. KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
  207. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  208. // Set the thread priority.
  209. thread->SetBasePriority(priority);
  210. return ResultSuccess;
  211. }
  212. Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
  213. return SetThreadPriority(system, thread_handle, priority);
  214. }
  215. Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
  216. u32 out_thread_ids_size, Handle debug_handle) {
  217. // TODO: Handle this case when debug events are supported.
  218. UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
  219. LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
  220. out_thread_ids, out_thread_ids_size);
  221. // If the size is negative or larger than INT32_MAX / sizeof(u64)
  222. if ((out_thread_ids_size & 0xF0000000) != 0) {
  223. LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
  224. out_thread_ids_size);
  225. return ResultOutOfRange;
  226. }
  227. auto* const current_process = system.Kernel().CurrentProcess();
  228. const auto total_copy_size = out_thread_ids_size * sizeof(u64);
  229. if (out_thread_ids_size > 0 &&
  230. !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
  231. LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
  232. out_thread_ids, out_thread_ids + total_copy_size);
  233. return ResultInvalidCurrentMemory;
  234. }
  235. auto& memory = system.Memory();
  236. const auto& thread_list = current_process->GetThreadList();
  237. const auto num_threads = thread_list.size();
  238. const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
  239. auto list_iter = thread_list.cbegin();
  240. for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
  241. memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
  242. out_thread_ids += sizeof(u64);
  243. }
  244. *out_num_threads = static_cast<u32>(num_threads);
  245. return ResultSuccess;
  246. }
  247. Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
  248. u64* out_affinity_mask) {
  249. LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
  250. // Get the thread from its handle.
  251. KScopedAutoObject thread =
  252. system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
  253. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  254. // Get the core mask.
  255. R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
  256. return ResultSuccess;
  257. }
  258. Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
  259. u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
  260. u64 out_affinity_mask{};
  261. const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
  262. *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
  263. *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
  264. return result;
  265. }
  266. Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
  267. u64 affinity_mask) {
  268. // Determine the core id/affinity mask.
  269. if (core_id == IdealCoreUseProcessValue) {
  270. core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
  271. affinity_mask = (1ULL << core_id);
  272. } else {
  273. // Validate the affinity mask.
  274. const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
  275. R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
  276. R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
  277. // Validate the core id.
  278. if (IsValidVirtualCoreId(core_id)) {
  279. R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
  280. } else {
  281. R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
  282. ResultInvalidCoreId);
  283. }
  284. }
  285. // Get the thread from its handle.
  286. KScopedAutoObject thread =
  287. system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
  288. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  289. // Set the core mask.
  290. R_TRY(thread->SetCoreMask(core_id, affinity_mask));
  291. return ResultSuccess;
  292. }
  293. Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
  294. u32 affinity_mask_low, u32 affinity_mask_high) {
  295. const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
  296. return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
  297. }
  298. /// Get the ID for the specified thread.
  299. Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
  300. // Get the thread from its handle.
  301. KScopedAutoObject thread =
  302. system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
  303. R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
  304. // Get the thread's id.
  305. *out_thread_id = thread->GetId();
  306. return ResultSuccess;
  307. }
  308. Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
  309. Handle thread_handle) {
  310. u64 out_thread_id{};
  311. const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
  312. *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
  313. *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
  314. return result;
  315. }
  316. } // namespace Kernel::Svc