svc.cpp 45 KB

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