svc.cpp 45 KB

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