svc.cpp 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. // Copyright 2014 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <map>
  5. #include "common/logging/log.h"
  6. #include "common/microprofile.h"
  7. #include "common/profiler.h"
  8. #include "common/string_util.h"
  9. #include "common/symbols.h"
  10. #include "core/core_timing.h"
  11. #include "core/arm/arm_interface.h"
  12. #include "core/hle/kernel/address_arbiter.h"
  13. #include "core/hle/kernel/event.h"
  14. #include "core/hle/kernel/memory.h"
  15. #include "core/hle/kernel/mutex.h"
  16. #include "core/hle/kernel/process.h"
  17. #include "core/hle/kernel/resource_limit.h"
  18. #include "core/hle/kernel/semaphore.h"
  19. #include "core/hle/kernel/shared_memory.h"
  20. #include "core/hle/kernel/thread.h"
  21. #include "core/hle/kernel/timer.h"
  22. #include "core/hle/kernel/vm_manager.h"
  23. #include "core/hle/function_wrappers.h"
  24. #include "core/hle/result.h"
  25. #include "core/hle/service/service.h"
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////
  27. // Namespace SVC
  28. using Kernel::SharedPtr;
  29. using Kernel::ERR_INVALID_HANDLE;
  30. namespace SVC {
  31. const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
  32. ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
  33. const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS,
  34. ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E
  35. const ResultCode ERR_MISALIGNED_ADDRESS{ // 0xE0E01BF1
  36. ErrorDescription::MisalignedAddress, ErrorModule::OS,
  37. ErrorSummary::InvalidArgument, ErrorLevel::Usage};
  38. const ResultCode ERR_MISALIGNED_SIZE{ // 0xE0E01BF2
  39. ErrorDescription::MisalignedSize, ErrorModule::OS,
  40. ErrorSummary::InvalidArgument, ErrorLevel::Usage};
  41. const ResultCode ERR_INVALID_COMBINATION{ // 0xE0E01BEE
  42. ErrorDescription::InvalidCombination, ErrorModule::OS,
  43. ErrorSummary::InvalidArgument, ErrorLevel::Usage};
  44. enum ControlMemoryOperation {
  45. MEMOP_FREE = 1,
  46. MEMOP_RESERVE = 2, // This operation seems to be unsupported in the kernel
  47. MEMOP_COMMIT = 3,
  48. MEMOP_MAP = 4,
  49. MEMOP_UNMAP = 5,
  50. MEMOP_PROTECT = 6,
  51. MEMOP_OPERATION_MASK = 0xFF,
  52. MEMOP_REGION_APP = 0x100,
  53. MEMOP_REGION_SYSTEM = 0x200,
  54. MEMOP_REGION_BASE = 0x300,
  55. MEMOP_REGION_MASK = 0xF00,
  56. MEMOP_LINEAR = 0x10000,
  57. };
  58. /// Map application or GSP heap memory
  59. static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) {
  60. using namespace Kernel;
  61. LOG_DEBUG(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=0x%X, permissions=0x%08X",
  62. operation, addr0, addr1, size, permissions);
  63. if ((addr0 & Memory::PAGE_MASK) != 0 || (addr1 & Memory::PAGE_MASK) != 0) {
  64. return ERR_MISALIGNED_ADDRESS;
  65. }
  66. if ((size & Memory::PAGE_MASK) != 0) {
  67. return ERR_MISALIGNED_SIZE;
  68. }
  69. u32 region = operation & MEMOP_REGION_MASK;
  70. operation &= ~MEMOP_REGION_MASK;
  71. if (region != 0) {
  72. LOG_WARNING(Kernel_SVC, "ControlMemory with specified region not supported, region=%X", region);
  73. }
  74. if ((permissions & (u32)MemoryPermission::ReadWrite) != permissions) {
  75. return ERR_INVALID_COMBINATION;
  76. }
  77. VMAPermission vma_permissions = (VMAPermission)permissions;
  78. auto& process = *g_current_process;
  79. switch (operation & MEMOP_OPERATION_MASK) {
  80. case MEMOP_FREE:
  81. {
  82. if (addr0 >= Memory::HEAP_VADDR && addr0 < Memory::HEAP_VADDR_END) {
  83. ResultCode result = process.HeapFree(addr0, size);
  84. if (result.IsError()) return result;
  85. } else if (addr0 >= process.GetLinearHeapBase() && addr0 < process.GetLinearHeapLimit()) {
  86. ResultCode result = process.LinearFree(addr0, size);
  87. if (result.IsError()) return result;
  88. } else {
  89. return ERR_INVALID_ADDRESS;
  90. }
  91. *out_addr = addr0;
  92. break;
  93. }
  94. case MEMOP_COMMIT:
  95. {
  96. if (operation & MEMOP_LINEAR) {
  97. CASCADE_RESULT(*out_addr, process.LinearAllocate(addr0, size, vma_permissions));
  98. } else {
  99. CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions));
  100. }
  101. break;
  102. }
  103. case MEMOP_MAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented
  104. {
  105. CASCADE_RESULT(*out_addr, process.HeapAllocate(addr0, size, vma_permissions));
  106. break;
  107. }
  108. case MEMOP_UNMAP: // TODO: This is just a hack to avoid regressions until memory aliasing is implemented
  109. {
  110. ResultCode result = process.HeapFree(addr0, size);
  111. if (result.IsError()) return result;
  112. break;
  113. }
  114. case MEMOP_PROTECT:
  115. {
  116. ResultCode result = process.vm_manager.ReprotectRange(addr0, size, vma_permissions);
  117. if (result.IsError()) return result;
  118. break;
  119. }
  120. default:
  121. LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation);
  122. return ERR_INVALID_COMBINATION;
  123. }
  124. process.vm_manager.LogLayout(Log::Level::Trace);
  125. return RESULT_SUCCESS;
  126. }
  127. /// Maps a memory block to specified address
  128. static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) {
  129. using Kernel::SharedMemory;
  130. using Kernel::MemoryPermission;
  131. LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
  132. handle, addr, permissions, other_permissions);
  133. // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
  134. SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
  135. if (shared_memory == nullptr)
  136. return ERR_INVALID_HANDLE;
  137. MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions);
  138. switch (permissions_type) {
  139. case MemoryPermission::Read:
  140. case MemoryPermission::Write:
  141. case MemoryPermission::ReadWrite:
  142. case MemoryPermission::Execute:
  143. case MemoryPermission::ReadExecute:
  144. case MemoryPermission::WriteExecute:
  145. case MemoryPermission::ReadWriteExecute:
  146. case MemoryPermission::DontCare:
  147. return shared_memory->Map(addr, permissions_type,
  148. static_cast<MemoryPermission>(other_permissions));
  149. default:
  150. LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
  151. }
  152. return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  153. }
  154. static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
  155. using Kernel::SharedMemory;
  156. LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X", handle, addr);
  157. // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
  158. SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
  159. if (shared_memory == nullptr)
  160. return ERR_INVALID_HANDLE;
  161. return shared_memory->Unmap(addr);
  162. }
  163. /// Connect to an OS service given the port name, returns the handle to the port to out
  164. static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) {
  165. if (port_name == nullptr)
  166. return ERR_NOT_FOUND;
  167. if (std::strlen(port_name) > 11)
  168. return ERR_PORT_NAME_TOO_LONG;
  169. LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name);
  170. auto it = Service::g_kernel_named_ports.find(port_name);
  171. if (it == Service::g_kernel_named_ports.end()) {
  172. LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name);
  173. return ERR_NOT_FOUND;
  174. }
  175. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second));
  176. return RESULT_SUCCESS;
  177. }
  178. /// Synchronize to an OS service
  179. static ResultCode SendSyncRequest(Handle handle) {
  180. SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
  181. if (session == nullptr) {
  182. return ERR_INVALID_HANDLE;
  183. }
  184. LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str());
  185. return session->SyncRequest().Code();
  186. }
  187. /// Close a handle
  188. static ResultCode CloseHandle(Handle handle) {
  189. LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle);
  190. return Kernel::g_handle_table.Close(handle);
  191. }
  192. /// Wait for a handle to synchronize, timeout after the specified nanoseconds
  193. static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) {
  194. auto object = Kernel::g_handle_table.GetWaitObject(handle);
  195. Kernel::Thread* thread = Kernel::GetCurrentThread();
  196. thread->waitsynch_waited = false;
  197. if (object == nullptr)
  198. return ERR_INVALID_HANDLE;
  199. LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
  200. object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
  201. HLE::Reschedule(__func__);
  202. // Check for next thread to schedule
  203. if (object->ShouldWait()) {
  204. object->AddWaitingThread(thread);
  205. Kernel::WaitCurrentThread_WaitSynchronization({ object }, false, false);
  206. // Create an event to wake the thread up after the specified nanosecond delay has passed
  207. thread->WakeAfterDelay(nano_seconds);
  208. // NOTE: output of this SVC will be set later depending on how the thread resumes
  209. return HLE::RESULT_INVALID;
  210. }
  211. object->Acquire();
  212. return RESULT_SUCCESS;
  213. }
  214. /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
  215. static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) {
  216. bool wait_thread = !wait_all;
  217. int handle_index = 0;
  218. Kernel::Thread* thread = Kernel::GetCurrentThread();
  219. bool was_waiting = thread->waitsynch_waited;
  220. thread->waitsynch_waited = false;
  221. // Check if 'handles' is invalid
  222. if (handles == nullptr)
  223. return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
  224. // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
  225. // this happens, the running application will crash.
  226. ASSERT_MSG(out != nullptr, "invalid output pointer specified!");
  227. // Check if 'handle_count' is invalid
  228. if (handle_count < 0)
  229. return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  230. // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if
  231. // necessary
  232. if (handle_count != 0) {
  233. bool selected = false; // True once an object has been selected
  234. Kernel::SharedPtr<Kernel::WaitObject> wait_object;
  235. for (int i = 0; i < handle_count; ++i) {
  236. auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
  237. if (object == nullptr)
  238. return ERR_INVALID_HANDLE;
  239. // Check if the current thread should wait on this object...
  240. if (object->ShouldWait()) {
  241. // Check we are waiting on all objects...
  242. if (wait_all)
  243. // Wait the thread
  244. wait_thread = true;
  245. } else {
  246. // Do not wait on this object, check if this object should be selected...
  247. if (!wait_all && (!selected || (wait_object == object && was_waiting))) {
  248. // Do not wait the thread
  249. wait_thread = false;
  250. handle_index = i;
  251. wait_object = object;
  252. selected = true;
  253. }
  254. }
  255. }
  256. } else {
  257. // If no handles were passed in, put the thread to sleep only when 'wait_all' is false
  258. // NOTE: This should deadlock the current thread if no timeout was specified
  259. if (!wait_all) {
  260. wait_thread = true;
  261. }
  262. }
  263. HLE::Reschedule(__func__);
  264. // If thread should wait, then set its state to waiting and then reschedule...
  265. if (wait_thread) {
  266. // Actually wait the current thread on each object if we decided to wait...
  267. std::vector<SharedPtr<Kernel::WaitObject>> wait_objects;
  268. wait_objects.reserve(handle_count);
  269. for (int i = 0; i < handle_count; ++i) {
  270. auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
  271. object->AddWaitingThread(Kernel::GetCurrentThread());
  272. wait_objects.push_back(object);
  273. }
  274. Kernel::WaitCurrentThread_WaitSynchronization(std::move(wait_objects), true, wait_all);
  275. // Create an event to wake the thread up after the specified nanosecond delay has passed
  276. Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds);
  277. // NOTE: output of this SVC will be set later depending on how the thread resumes
  278. return HLE::RESULT_INVALID;
  279. }
  280. // Acquire objects if we did not wait...
  281. for (int i = 0; i < handle_count; ++i) {
  282. auto object = Kernel::g_handle_table.GetWaitObject(handles[i]);
  283. // Acquire the object if it is not waiting...
  284. if (!object->ShouldWait()) {
  285. object->Acquire();
  286. // If this was the first non-waiting object and 'wait_all' is false, don't acquire
  287. // any other objects
  288. if (!wait_all)
  289. break;
  290. }
  291. }
  292. // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does
  293. // not seem to set it to any meaningful value.
  294. *out = handle_count != 0 ? (wait_all ? -1 : handle_index) : 0;
  295. return RESULT_SUCCESS;
  296. }
  297. /// Create an address arbiter (to allocate access to shared resources)
  298. static ResultCode CreateAddressArbiter(Handle* out_handle) {
  299. using Kernel::AddressArbiter;
  300. SharedPtr<AddressArbiter> arbiter = AddressArbiter::Create();
  301. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(arbiter)));
  302. LOG_TRACE(Kernel_SVC, "returned handle=0x%08X", *out_handle);
  303. return RESULT_SUCCESS;
  304. }
  305. /// Arbitrate address
  306. static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds) {
  307. using Kernel::AddressArbiter;
  308. LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", handle,
  309. address, type, value);
  310. SharedPtr<AddressArbiter> arbiter = Kernel::g_handle_table.Get<AddressArbiter>(handle);
  311. if (arbiter == nullptr)
  312. return ERR_INVALID_HANDLE;
  313. auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type),
  314. address, value, nanoseconds);
  315. return res;
  316. }
  317. static void Break(u8 break_reason) {
  318. LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!");
  319. std::string reason_str;
  320. switch (break_reason) {
  321. case 0: reason_str = "PANIC"; break;
  322. case 1: reason_str = "ASSERT"; break;
  323. case 2: reason_str = "USER"; break;
  324. default: reason_str = "UNKNOWN"; break;
  325. }
  326. LOG_CRITICAL(Debug_Emulated, "Break reason: %s", reason_str.c_str());
  327. }
  328. /// Used to output a message on a debug hardware unit - does nothing on a retail unit
  329. static void OutputDebugString(const char* string) {
  330. LOG_DEBUG(Debug_Emulated, "%s", string);
  331. }
  332. /// Get resource limit
  333. static ResultCode GetResourceLimit(Handle* resource_limit, Handle process_handle) {
  334. LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle);
  335. SharedPtr<Kernel::Process> process = Kernel::g_handle_table.Get<Kernel::Process>(process_handle);
  336. if (process == nullptr)
  337. return ERR_INVALID_HANDLE;
  338. CASCADE_RESULT(*resource_limit, Kernel::g_handle_table.Create(process->resource_limit));
  339. return RESULT_SUCCESS;
  340. }
  341. /// Get resource limit current values
  342. static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit_handle, u32* names,
  343. u32 name_count) {
  344. LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d",
  345. resource_limit_handle, names, name_count);
  346. SharedPtr<Kernel::ResourceLimit> resource_limit = Kernel::g_handle_table.Get<Kernel::ResourceLimit>(resource_limit_handle);
  347. if (resource_limit == nullptr)
  348. return ERR_INVALID_HANDLE;
  349. for (unsigned int i = 0; i < name_count; ++i)
  350. values[i] = resource_limit->GetCurrentResourceValue(names[i]);
  351. return RESULT_SUCCESS;
  352. }
  353. /// Get resource limit max values
  354. static ResultCode GetResourceLimitLimitValues(s64* values, Handle resource_limit_handle, u32* names,
  355. u32 name_count) {
  356. LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d",
  357. resource_limit_handle, names, name_count);
  358. SharedPtr<Kernel::ResourceLimit> resource_limit = Kernel::g_handle_table.Get<Kernel::ResourceLimit>(resource_limit_handle);
  359. if (resource_limit == nullptr)
  360. return ERR_INVALID_HANDLE;
  361. for (unsigned int i = 0; i < name_count; ++i)
  362. values[i] = resource_limit->GetMaxResourceValue(names[i]);
  363. return RESULT_SUCCESS;
  364. }
  365. /// Creates a new thread
  366. static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
  367. using Kernel::Thread;
  368. std::string name;
  369. if (Symbols::HasSymbol(entry_point)) {
  370. TSymbol symbol = Symbols::GetSymbol(entry_point);
  371. name = symbol.name;
  372. } else {
  373. name = Common::StringFromFormat("unknown-%08x", entry_point);
  374. }
  375. // TODO(bunnei): Implement resource limits to return an error code instead of the below assert.
  376. // The error code should be: Description::NotAuthorized, Module::OS, Summary::WrongArgument,
  377. // Level::Permanent
  378. ASSERT_MSG(priority >= THREADPRIO_USERLAND_MAX, "Unexpected thread priority!");
  379. if (priority > THREADPRIO_LOWEST) {
  380. return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS,
  381. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  382. }
  383. switch (processor_id) {
  384. case THREADPROCESSORID_ALL:
  385. case THREADPROCESSORID_DEFAULT:
  386. case THREADPROCESSORID_0:
  387. case THREADPROCESSORID_1:
  388. break;
  389. default:
  390. // TODO(bunnei): Implement support for other processor IDs
  391. ASSERT_MSG(false, "Unsupported thread processor ID: %d", processor_id);
  392. break;
  393. }
  394. CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(
  395. name, entry_point, priority, arg, processor_id, stack_top));
  396. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread)));
  397. LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
  398. "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
  399. name.c_str(), arg, stack_top, priority, processor_id, *out_handle);
  400. return RESULT_SUCCESS;
  401. }
  402. /// Called when a thread exits
  403. static void ExitThread() {
  404. LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC());
  405. Kernel::GetCurrentThread()->Stop();
  406. }
  407. /// Gets the priority for the specified thread
  408. static ResultCode GetThreadPriority(s32* priority, Handle handle) {
  409. const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
  410. if (thread == nullptr)
  411. return ERR_INVALID_HANDLE;
  412. *priority = thread->GetPriority();
  413. return RESULT_SUCCESS;
  414. }
  415. /// Sets the priority for the specified thread
  416. static ResultCode SetThreadPriority(Handle handle, s32 priority) {
  417. SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
  418. if (thread == nullptr)
  419. return ERR_INVALID_HANDLE;
  420. thread->SetPriority(priority);
  421. return RESULT_SUCCESS;
  422. }
  423. /// Create a mutex
  424. static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) {
  425. using Kernel::Mutex;
  426. SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0);
  427. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex)));
  428. LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X",
  429. initial_locked ? "true" : "false", *out_handle);
  430. return RESULT_SUCCESS;
  431. }
  432. /// Release a mutex
  433. static ResultCode ReleaseMutex(Handle handle) {
  434. using Kernel::Mutex;
  435. LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle);
  436. SharedPtr<Mutex> mutex = Kernel::g_handle_table.Get<Mutex>(handle);
  437. if (mutex == nullptr)
  438. return ERR_INVALID_HANDLE;
  439. mutex->Release();
  440. return RESULT_SUCCESS;
  441. }
  442. /// Get the ID of the specified process
  443. static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
  444. LOG_TRACE(Kernel_SVC, "called process=0x%08X", process_handle);
  445. const SharedPtr<Kernel::Process> process = Kernel::g_handle_table.Get<Kernel::Process>(process_handle);
  446. if (process == nullptr)
  447. return ERR_INVALID_HANDLE;
  448. *process_id = process->process_id;
  449. return RESULT_SUCCESS;
  450. }
  451. /// Get the ID of the process that owns the specified thread
  452. static ResultCode GetProcessIdOfThread(u32* process_id, Handle thread_handle) {
  453. LOG_TRACE(Kernel_SVC, "called thread=0x%08X", thread_handle);
  454. const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(thread_handle);
  455. if (thread == nullptr)
  456. return ERR_INVALID_HANDLE;
  457. const SharedPtr<Kernel::Process> process = thread->owner_process;
  458. ASSERT_MSG(process != nullptr, "Invalid parent process for thread=0x%08X", thread_handle);
  459. *process_id = process->process_id;
  460. return RESULT_SUCCESS;
  461. }
  462. /// Get the ID for the specified thread.
  463. static ResultCode GetThreadId(u32* thread_id, Handle handle) {
  464. LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
  465. const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
  466. if (thread == nullptr)
  467. return ERR_INVALID_HANDLE;
  468. *thread_id = thread->GetThreadId();
  469. return RESULT_SUCCESS;
  470. }
  471. /// Creates a semaphore
  472. static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
  473. using Kernel::Semaphore;
  474. CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count));
  475. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore)));
  476. LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X",
  477. initial_count, max_count, *out_handle);
  478. return RESULT_SUCCESS;
  479. }
  480. /// Releases a certain number of slots in a semaphore
  481. static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
  482. using Kernel::Semaphore;
  483. LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, handle);
  484. SharedPtr<Semaphore> semaphore = Kernel::g_handle_table.Get<Semaphore>(handle);
  485. if (semaphore == nullptr)
  486. return ERR_INVALID_HANDLE;
  487. CASCADE_RESULT(*count, semaphore->Release(release_count));
  488. return RESULT_SUCCESS;
  489. }
  490. /// Query process memory
  491. static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* page_info, Handle process_handle, u32 addr) {
  492. using Kernel::Process;
  493. Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle);
  494. if (process == nullptr)
  495. return ERR_INVALID_HANDLE;
  496. auto vma = process->vm_manager.FindVMA(addr);
  497. if (vma == Kernel::g_current_process->vm_manager.vma_map.end())
  498. return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  499. memory_info->base_address = vma->second.base;
  500. memory_info->permission = static_cast<u32>(vma->second.permissions);
  501. memory_info->size = vma->second.size;
  502. memory_info->state = static_cast<u32>(vma->second.meminfo_state);
  503. page_info->flags = 0;
  504. LOG_TRACE(Kernel_SVC, "called process=0x%08X addr=0x%08X", process_handle, addr);
  505. return RESULT_SUCCESS;
  506. }
  507. /// Query memory
  508. static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 addr) {
  509. return QueryProcessMemory(memory_info, page_info, Kernel::CurrentProcess, addr);
  510. }
  511. /// Create an event
  512. static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) {
  513. using Kernel::Event;
  514. SharedPtr<Event> evt = Kernel::Event::Create(static_cast<ResetType>(reset_type));
  515. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt)));
  516. LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
  517. reset_type, *out_handle);
  518. return RESULT_SUCCESS;
  519. }
  520. /// Duplicates a kernel handle
  521. static ResultCode DuplicateHandle(Handle* out, Handle handle) {
  522. CASCADE_RESULT(*out, Kernel::g_handle_table.Duplicate(handle));
  523. LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out);
  524. return RESULT_SUCCESS;
  525. }
  526. /// Signals an event
  527. static ResultCode SignalEvent(Handle handle) {
  528. using Kernel::Event;
  529. LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle);
  530. SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle);
  531. if (evt == nullptr)
  532. return ERR_INVALID_HANDLE;
  533. evt->Signal();
  534. return RESULT_SUCCESS;
  535. }
  536. /// Clears an event
  537. static ResultCode ClearEvent(Handle handle) {
  538. using Kernel::Event;
  539. LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle);
  540. SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle);
  541. if (evt == nullptr)
  542. return ERR_INVALID_HANDLE;
  543. evt->Clear();
  544. return RESULT_SUCCESS;
  545. }
  546. /// Creates a timer
  547. static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) {
  548. using Kernel::Timer;
  549. SharedPtr<Timer> timer = Timer::Create(static_cast<ResetType>(reset_type));
  550. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer)));
  551. LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
  552. reset_type, *out_handle);
  553. return RESULT_SUCCESS;
  554. }
  555. /// Clears a timer
  556. static ResultCode ClearTimer(Handle handle) {
  557. using Kernel::Timer;
  558. LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
  559. SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
  560. if (timer == nullptr)
  561. return ERR_INVALID_HANDLE;
  562. timer->Clear();
  563. return RESULT_SUCCESS;
  564. }
  565. /// Starts a timer
  566. static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
  567. using Kernel::Timer;
  568. LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
  569. SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
  570. if (timer == nullptr)
  571. return ERR_INVALID_HANDLE;
  572. timer->Set(initial, interval);
  573. return RESULT_SUCCESS;
  574. }
  575. /// Cancels a timer
  576. static ResultCode CancelTimer(Handle handle) {
  577. using Kernel::Timer;
  578. LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
  579. SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
  580. if (timer == nullptr)
  581. return ERR_INVALID_HANDLE;
  582. timer->Cancel();
  583. return RESULT_SUCCESS;
  584. }
  585. /// Sleep the current thread
  586. static void SleepThread(s64 nanoseconds) {
  587. LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
  588. // Sleep current thread and check for next thread to schedule
  589. Kernel::WaitCurrentThread_Sleep();
  590. // Create an event to wake the thread up after the specified nanosecond delay has passed
  591. Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds);
  592. }
  593. /// This returns the total CPU ticks elapsed since the CPU was powered-on
  594. static s64 GetSystemTick() {
  595. s64 result = CoreTiming::GetTicks();
  596. // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
  597. Core::g_app_core->AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
  598. return result;
  599. }
  600. /// Creates a memory block at the specified address with the specified permissions and size
  601. static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
  602. u32 other_permission) {
  603. using Kernel::SharedMemory;
  604. if (size % Memory::PAGE_SIZE != 0)
  605. return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  606. // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
  607. // TODO(Subv): Implement this function properly
  608. using Kernel::MemoryPermission;
  609. SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size,
  610. (MemoryPermission)my_permission, (MemoryPermission)other_permission);
  611. // Map the SharedMemory to the specified address
  612. shared_memory->base_address = addr;
  613. CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory)));
  614. LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
  615. return RESULT_SUCCESS;
  616. }
  617. static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
  618. using Kernel::MemoryRegion;
  619. LOG_TRACE(Kernel_SVC, "called type=%u param=%d", type, param);
  620. switch ((SystemInfoType)type) {
  621. case SystemInfoType::REGION_MEMORY_USAGE:
  622. switch ((SystemInfoMemUsageRegion)param) {
  623. case SystemInfoMemUsageRegion::ALL:
  624. *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used
  625. + Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used
  626. + Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
  627. break;
  628. case SystemInfoMemUsageRegion::APPLICATION:
  629. *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used;
  630. break;
  631. case SystemInfoMemUsageRegion::SYSTEM:
  632. *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used;
  633. break;
  634. case SystemInfoMemUsageRegion::BASE:
  635. *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
  636. break;
  637. default:
  638. LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=0 region: param=%d", param);
  639. *out = 0;
  640. break;
  641. }
  642. break;
  643. case SystemInfoType::KERNEL_ALLOCATED_PAGES:
  644. LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", param);
  645. *out = 0;
  646. break;
  647. case SystemInfoType::KERNEL_SPAWNED_PIDS:
  648. *out = 5;
  649. break;
  650. default:
  651. LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=%u param=%d", type, param);
  652. *out = 0;
  653. break;
  654. }
  655. // This function never returns an error, even if invalid parameters were passed.
  656. return RESULT_SUCCESS;
  657. }
  658. static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) {
  659. LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type);
  660. using Kernel::Process;
  661. Kernel::SharedPtr<Process> process = Kernel::g_handle_table.Get<Process>(process_handle);
  662. if (process == nullptr)
  663. return ERR_INVALID_HANDLE;
  664. switch (type) {
  665. case 0:
  666. case 2:
  667. // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure
  668. // what's the difference between them.
  669. *out = process->heap_used + process->linear_heap_used + process->misc_memory_used;
  670. break;
  671. case 1:
  672. case 3:
  673. case 4:
  674. case 5:
  675. case 6:
  676. case 7:
  677. case 8:
  678. // These are valid, but not implemented yet
  679. LOG_ERROR(Kernel_SVC, "unimplemented GetProcessInfo type=%u", type);
  680. break;
  681. case 20:
  682. *out = Memory::FCRAM_PADDR - process->GetLinearHeapBase();
  683. break;
  684. default:
  685. LOG_ERROR(Kernel_SVC, "unknown GetProcessInfo type=%u", type);
  686. if (type >= 21 && type <= 23) {
  687. return ResultCode( // 0xE0E01BF4
  688. ErrorDescription::NotImplemented, ErrorModule::OS,
  689. ErrorSummary::InvalidArgument, ErrorLevel::Usage);
  690. } else {
  691. return ResultCode( // 0xD8E007ED
  692. ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
  693. ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
  694. }
  695. break;
  696. }
  697. return RESULT_SUCCESS;
  698. }
  699. namespace {
  700. struct FunctionDef {
  701. using Func = void();
  702. u32 id;
  703. Func* func;
  704. const char* name;
  705. };
  706. }
  707. static const FunctionDef SVC_Table[] = {
  708. {0x00, nullptr, "Unknown"},
  709. {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
  710. {0x02, HLE::Wrap<QueryMemory>, "QueryMemory"},
  711. {0x03, nullptr, "ExitProcess"},
  712. {0x04, nullptr, "GetProcessAffinityMask"},
  713. {0x05, nullptr, "SetProcessAffinityMask"},
  714. {0x06, nullptr, "GetProcessIdealProcessor"},
  715. {0x07, nullptr, "SetProcessIdealProcessor"},
  716. {0x08, HLE::Wrap<CreateThread>, "CreateThread"},
  717. {0x09, ExitThread, "ExitThread"},
  718. {0x0A, HLE::Wrap<SleepThread>, "SleepThread"},
  719. {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"},
  720. {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"},
  721. {0x0D, nullptr, "GetThreadAffinityMask"},
  722. {0x0E, nullptr, "SetThreadAffinityMask"},
  723. {0x0F, nullptr, "GetThreadIdealProcessor"},
  724. {0x10, nullptr, "SetThreadIdealProcessor"},
  725. {0x11, nullptr, "GetCurrentProcessorNumber"},
  726. {0x12, nullptr, "Run"},
  727. {0x13, HLE::Wrap<CreateMutex>, "CreateMutex"},
  728. {0x14, HLE::Wrap<ReleaseMutex>, "ReleaseMutex"},
  729. {0x15, HLE::Wrap<CreateSemaphore>, "CreateSemaphore"},
  730. {0x16, HLE::Wrap<ReleaseSemaphore>, "ReleaseSemaphore"},
  731. {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
  732. {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
  733. {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
  734. {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"},
  735. {0x1B, HLE::Wrap<SetTimer>, "SetTimer"},
  736. {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"},
  737. {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"},
  738. {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
  739. {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
  740. {0x20, HLE::Wrap<UnmapMemoryBlock>, "UnmapMemoryBlock"},
  741. {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},
  742. {0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"},
  743. {0x23, HLE::Wrap<CloseHandle>, "CloseHandle"},
  744. {0x24, HLE::Wrap<WaitSynchronization1>, "WaitSynchronization1"},
  745. {0x25, HLE::Wrap<WaitSynchronizationN>, "WaitSynchronizationN"},
  746. {0x26, nullptr, "SignalAndWait"},
  747. {0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"},
  748. {0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"},
  749. {0x29, nullptr, "GetHandleInfo"},
  750. {0x2A, HLE::Wrap<GetSystemInfo>, "GetSystemInfo"},
  751. {0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"},
  752. {0x2C, nullptr, "GetThreadInfo"},
  753. {0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"},
  754. {0x2E, nullptr, "SendSyncRequest1"},
  755. {0x2F, nullptr, "SendSyncRequest2"},
  756. {0x30, nullptr, "SendSyncRequest3"},
  757. {0x31, nullptr, "SendSyncRequest4"},
  758. {0x32, HLE::Wrap<SendSyncRequest>, "SendSyncRequest"},
  759. {0x33, nullptr, "OpenProcess"},
  760. {0x34, nullptr, "OpenThread"},
  761. {0x35, HLE::Wrap<GetProcessId>, "GetProcessId"},
  762. {0x36, HLE::Wrap<GetProcessIdOfThread>, "GetProcessIdOfThread"},
  763. {0x37, HLE::Wrap<GetThreadId>, "GetThreadId"},
  764. {0x38, HLE::Wrap<GetResourceLimit>, "GetResourceLimit"},
  765. {0x39, HLE::Wrap<GetResourceLimitLimitValues>, "GetResourceLimitLimitValues"},
  766. {0x3A, HLE::Wrap<GetResourceLimitCurrentValues>, "GetResourceLimitCurrentValues"},
  767. {0x3B, nullptr, "GetThreadContext"},
  768. {0x3C, HLE::Wrap<Break>, "Break"},
  769. {0x3D, HLE::Wrap<OutputDebugString>, "OutputDebugString"},
  770. {0x3E, nullptr, "ControlPerformanceCounter"},
  771. {0x3F, nullptr, "Unknown"},
  772. {0x40, nullptr, "Unknown"},
  773. {0x41, nullptr, "Unknown"},
  774. {0x42, nullptr, "Unknown"},
  775. {0x43, nullptr, "Unknown"},
  776. {0x44, nullptr, "Unknown"},
  777. {0x45, nullptr, "Unknown"},
  778. {0x46, nullptr, "Unknown"},
  779. {0x47, nullptr, "CreatePort"},
  780. {0x48, nullptr, "CreateSessionToPort"},
  781. {0x49, nullptr, "CreateSession"},
  782. {0x4A, nullptr, "AcceptSession"},
  783. {0x4B, nullptr, "ReplyAndReceive1"},
  784. {0x4C, nullptr, "ReplyAndReceive2"},
  785. {0x4D, nullptr, "ReplyAndReceive3"},
  786. {0x4E, nullptr, "ReplyAndReceive4"},
  787. {0x4F, nullptr, "ReplyAndReceive"},
  788. {0x50, nullptr, "BindInterrupt"},
  789. {0x51, nullptr, "UnbindInterrupt"},
  790. {0x52, nullptr, "InvalidateProcessDataCache"},
  791. {0x53, nullptr, "StoreProcessDataCache"},
  792. {0x54, nullptr, "FlushProcessDataCache"},
  793. {0x55, nullptr, "StartInterProcessDma"},
  794. {0x56, nullptr, "StopDma"},
  795. {0x57, nullptr, "GetDmaState"},
  796. {0x58, nullptr, "RestartDma"},
  797. {0x59, nullptr, "Unknown"},
  798. {0x5A, nullptr, "Unknown"},
  799. {0x5B, nullptr, "Unknown"},
  800. {0x5C, nullptr, "Unknown"},
  801. {0x5D, nullptr, "Unknown"},
  802. {0x5E, nullptr, "Unknown"},
  803. {0x5F, nullptr, "Unknown"},
  804. {0x60, nullptr, "DebugActiveProcess"},
  805. {0x61, nullptr, "BreakDebugProcess"},
  806. {0x62, nullptr, "TerminateDebugProcess"},
  807. {0x63, nullptr, "GetProcessDebugEvent"},
  808. {0x64, nullptr, "ContinueDebugEvent"},
  809. {0x65, nullptr, "GetProcessList"},
  810. {0x66, nullptr, "GetThreadList"},
  811. {0x67, nullptr, "GetDebugThreadContext"},
  812. {0x68, nullptr, "SetDebugThreadContext"},
  813. {0x69, nullptr, "QueryDebugProcessMemory"},
  814. {0x6A, nullptr, "ReadProcessMemory"},
  815. {0x6B, nullptr, "WriteProcessMemory"},
  816. {0x6C, nullptr, "SetHardwareBreakPoint"},
  817. {0x6D, nullptr, "GetDebugThreadParam"},
  818. {0x6E, nullptr, "Unknown"},
  819. {0x6F, nullptr, "Unknown"},
  820. {0x70, nullptr, "ControlProcessMemory"},
  821. {0x71, nullptr, "MapProcessMemory"},
  822. {0x72, nullptr, "UnmapProcessMemory"},
  823. {0x73, nullptr, "CreateCodeSet"},
  824. {0x74, nullptr, "RandomStub"},
  825. {0x75, nullptr, "CreateProcess"},
  826. {0x76, nullptr, "TerminateProcess"},
  827. {0x77, nullptr, "SetProcessResourceLimits"},
  828. {0x78, nullptr, "CreateResourceLimit"},
  829. {0x79, nullptr, "SetResourceLimitValues"},
  830. {0x7A, nullptr, "AddCodeSegment"},
  831. {0x7B, nullptr, "Backdoor"},
  832. {0x7C, nullptr, "KernelSetState"},
  833. {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"},
  834. };
  835. Common::Profiling::TimingCategory profiler_svc("SVC Calls");
  836. static const FunctionDef* GetSVCInfo(u32 func_num) {
  837. if (func_num >= ARRAY_SIZE(SVC_Table)) {
  838. LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num);
  839. return nullptr;
  840. }
  841. return &SVC_Table[func_num];
  842. }
  843. MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
  844. void CallSVC(u32 immediate) {
  845. Common::Profiling::ScopeTimer timer_svc(profiler_svc);
  846. MICROPROFILE_SCOPE(Kernel_SVC);
  847. const FunctionDef* info = GetSVCInfo(immediate);
  848. if (info) {
  849. if (info->func) {
  850. info->func();
  851. } else {
  852. LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name);
  853. }
  854. }
  855. }
  856. } // namespace