System calls

Id Name In Out
0x1 #svcSetHeapSize W1=size W0=result, X1=outaddr
0x2 #svcSetMemoryPermission X0=addr, X1=size, W2=prot W0=result
0x3 #svcSetMemoryAttribute X0=addr, X1=size, W2=state0, W3=state1 W0=result
0x4 #svcMapMemory X0=dstaddr, X1=srcaddr, X2=size W0=result
0x5 #svcUnmapMemory X0=dstaddr, X1=srcaddr, X2=size W0=result
0x6 #svcQueryMemory X0=MemoryInfo*, X2=addr W0=result, W1=PageInfo
0x7 #svcExitProcess None
0x8 #svcCreateThread X1=entry, X2=thread_context, X3=stacktop, W4=prio, W5=processor_id

R0=prio, R1=entry, R2=thread_context, R3=stacktop, R4=processor_id

W0=result, W1=handle
0x9 #svcStartThread W0=thread_handle W0=result
0xA #svcExitThread None
0xB #svcSleepThread X0=nano

R0=nano_lower32, R1=lower_upper32

0xC #svcGetThreadPriority W1=thread_handle W0=result, W1=prio
0xD #svcSetThreadPriority W0=thread_handle, W1=prio W0=result
0xE #svcGetThreadCoreMask W2=thread_handle W0=result, W1=out0, X2=out1

R0=result, R1=out0, R2=out1_lower32, R3=out1_upper32

0xF #svcSetThreadCoreMask W0=thread_handle, W1=in, X2=in2

R0=thread_handle, R1=in, R2=in2_lower32, R3=in2_upper32

W0=result
0x10 #svcGetCurrentProcessorNumber None W0/X0=cpuid
0x11 #svcSignalEvent W0=wevent_handle W0=result
0x12 #svcClearEvent W0=wevent_or_revent_handle W0=result
0x13 #svcMapSharedMemory W0=shmem_handle, X1=addr, X2=size, W3=perm W0=result
0x14 svcUnmapSharedMemory W0=shmem_handle, X1=addr, X2=size W0=result
0x15 #svcCreateTransferMemory X1=addr, X2=size, W3=perm W0=result, W1=tmem_handle
0x16 svcCloseHandle W0=handle W0=result
0x17 #svcResetSignal W0=revent_or_process_handle W0=result
0x18 #svcWaitSynchronization X1=handles_ptr, W2=num_handles, X3=timeout

R0=timeout_lower32, R1=handles_ptr, R2=num_handles, R3=timeout_upper32

W0=result, W1=handle_idx
0x19 #svcCancelSynchronization W0=thread_handle W0=result
0x1A svcArbitrateLock W0=cur_thread_handle, X1=ptr, W2=req_thread_handle
0x1B svcArbitrateUnlock X0=ptr
0x1C svcWaitProcessWideKeyAtomic X0=ptr0, X1=ptr, W2=thread_handle, X3=timeout

R0=ptr0, R1=ptr, R2=thread_handle, R3=timeout_lower32, R4=timeout_upper32

W0=result
0x1D svcSignalProcessWideKey X0=ptr, W1=value W0=result
0x1E #svcGetSystemTick None X0={value of cntpct_el0}

R0=cntpct_el0_lower32, R1=cntpct_el0_upper32

0x1F svcConnectToNamedPort X1=port_name_str W0=result, W1=handle
0x20 svcSendSyncRequestLight W0=light_session_handle, X1=? W0=result
0x21 svcSendSyncRequest X0=normal_session_handle W0=result
0x22 #svcSendSyncRequestWithUserBuffer X0=cmdbufptr, X1=size, X2=handle W0=result
0x23 svcSendAsyncRequestWithUserBuffer X1=cmdbufptr, X2=size, X3=handle W0=result, W1=revent_handle
0x24 svcGetProcessId W1=thread_or_process_or_debug_handle W0=result, X1=pid

R0=result, R1=pid_lower32, R2=pid_upper32

0x25 svcGetThreadId W1=thread_handle W0=result, X1=out

R0=result, R1=out_lower32, R2=out_upper32

0x26 #svcBreak X0=break_reason,X1,X2=info W0=result = 0
0x27 svcOutputDebugString X0=str, X1=size W0=result
0x28 svcReturnFromException X0=result
0x29 #svcGetInfo W1=info_id, X2=handle, X3=info_sub_id

R0=info_sub_id_lower32, R1=info_id, R2=handle, R3=info_sub_id_upper32

W0=result, X1=out

R0=result, R1=out_lower32, R2=out_upper32

0x2A svcFlushEntireDataCache None None
0x2B svcFlushDataCache X0=addr, X1=size W0=result
0x2C [3.0.0+] #svcMapPhysicalMemory X0=addr, X1=size W0=result
0x2D [3.0.0+] svcUnmapPhysicalMemory X0=addr, X1=size W0=result
0x2E [5.0.0+] svcGetFutureThreadInfo X3=timeout

R0=timeout_lower32, R1=timeout_upper32

W0=result, bunch of crap
0x2F svcGetLastThreadInfo None W0=result, W1,W2,W3,W4=unk, W5=truncated_u64, W6=bool
0x30 svcGetResourceLimitLimitValue W1=reslimit_handle, W2=#LimitableResource W0=result, X1=value

R0=result, R1=value_lower32, R2=value_upper32

0x31 svcGetResourceLimitCurrentValue W1=reslimit_handle, W2=#LimitableResource W0=result, X1=value

R0=result, R1=value_lower32, R2=value_upper32

0x32 svcSetThreadActivity W0=thread_handle, W1=bool W0=result
0x33 svcGetThreadContext3 X0=#ThreadContext*, W1=thread_handle W0=result
0x34 [4.0.0+] svcWaitForAddress X0=ptr, W1=#ArbitrationType, X2=value, X3=timeout

R0=ptr, R1=#ArbitrationType, R2=value, R3=timeout_lower32, R4=timeout_upper32

0x35 [4.0.0+] svcSignalToAddress X0=ptr, W1=#SignalType, X2=value, W3=num_to_signal
0x36 [8.0.0+] svcSynchronizePreemptionState None W0=result
0x3C #svcDumpInfo
0x3D [4.0.0+] svcDumpInfoNew
0x40 svcCreateSession W2=is_light, X3=name_ptr W0=result, W1=server_handle, W2=client_handle
0x41 #svcAcceptSession W1=port_handle W0=result, W1=session_handle
0x42 svcReplyAndReceiveLight W0=light_session_handle W0=result, W1,W2,W3,W4,W5,W6,W7=out
0x43 #svcReplyAndReceive X1=ptr_handles, W2=num_handles, X3=replytarget_handle(0=none), X4=timeout

R0=timeout_lower32, R1=ptr_handles, R2=num_handles, R3=replytarget_handle(0=none), R4=timeout_upper32

W0=result, W1=handle_idx
0x44 svcReplyAndReceiveWithUserBuffer X1=buf, X2=sz, X3=ptr_handles, W4=num_handles, X5=replytarget_handle(0=none), X6=timeout

R0=num_handles, R1=buf, R2=sz, R3=ptr_handles, R4=replytarget_handle(0=none), R5=timeout_lower32, R6=timeout_upper32

W0=result, W1=handle_idx
0x45 svcCreateEvent None W0=result, W1=wevent_handle, W2=revent_handle
0x48 [5.0.0+] #svcMapPhysicalMemoryUnsafe X0=addr, X1=size W0=result
0x49 [5.0.0+] svcUnmapPhysicalMemoryUnsafe X0=addr, X1=size W0=result
0x4A [5.0.0+] svcSetUnsafeLimit X0=size W0=result
0x4B [4.0.0+] #svcCreateCodeMemory X1=addr, X2=size W0=result, W1=code_memory_handle
0x4C [4.0.0+] #svcControlCodeMemory W0=code_memory_handle, W1=#CodeMemoryOperation, X2=dstaddr, X3=size, W4=perm

R0=code_memory_handle, R1=#CodeMemoryOperation, R2=dstaddr_lower32, R3=dstaddr_upper32, R4=size_lower32, R5=size_upper32, R6=perm

W0=result
0x4D svcSleepSystem None None
0x4E #svcReadWriteRegister X1=reg_addr, W2=rw_mask, W3=in_val

R0=rw_mask, R1=in_val, R2=reg_addr_lower32, R3=reg_addr_upper32

W0=result, W1=out_val
0x4F svcSetProcessActivity W0=process_handle, W1=bool W0=result
0x50 #svcCreateSharedMemory W1=size, W2=myperm, W3=otherperm W0=result, W1=shmem_handle
0x51 #svcMapTransferMemory X0=tmem_handle, X1=addr, X2=size, W3=perm W0=result
0x52 #svcUnmapTransferMemory W0=tmemhandle, X1=addr, X2=size W0=result
0x53 #svcCreateInterruptEvent X1=irq_num, W2=flag W0=result, W1=handle
0x54 #svcQueryPhysicalAddress X1=addr W0=result, X1=physaddr, X2=baseaddr, X3=size
0x55 #svcQueryIoMapping X1=physaddr, X2=size

R0=size, R2=physaddr_lower32, R3=physaddr_upper32

W0=result, X1=virtaddr
0x56 #svcCreateDeviceAddressSpace X1=dev_as_start_addr, X2=dev_as_end_addr

R0=dev_as_end_addr_lower32, R1=dev_as_end_addr_upper32, R2=dev_as_start_addr_lower32, R3=dev_as_start_addr_upper32

W0=result, W1=dev_as_handle
0x57 #svcAttachDeviceAddressSpace W0=device, X1=dev_as_handle W0=result
0x58 #svcDetachDeviceAddressSpace W0=device, X1=dev_as_handle W0=result
0x59 #svcMapDeviceAddressSpaceByForce W0=dev_as_handle, W1=proc_handle, X2=dev_map_addr, X3=dev_as_size, X4=dev_as_addr, W5=perm

R0=dev_as_handle, R1=proc_handle, R2=dev_map_addr_lower32, R3=dev_map_addr_upper32, R4=rev_as_size, R5=dev_as_addr_lower32, R6=dev_as_addr_upper32, R7=perm

W0=result
0x5A #svcMapDeviceAddressSpaceAligned W0=dev_as_handle, W1=proc_handle, X2=dev_map_addr, X3=dev_as_size, X4=dev_as_addr, W5=perm

R0=dev_as_handle, R1=proc_handle, R2=dev_map_addr_lower32, R3=dev_map_addr_upper32, R4=rev_as_size, R5=dev_as_addr_lower32, R6=dev_as_addr_upper32, R7=perm

W0=result
0x5B svcMapDeviceAddressSpace W1=dev_as_handle, W2=proc_handle, X3=dev_map_addr, X4=dev_as_size, X5=dev_as_addr, W6=perm

R0=dev_map_addr_lower32, R1=dev_as_handle, R2=proc_handle, R3=dev_map_addr_upper32, R4=dev_as_size, R5=dev_as_addr_lower32, R6=dev_as_addr_upper32, R7=perm

W0=result, X1=mapped_size

R0=result, R1=mapped_size

0x5C #svcUnmapDeviceAddressSpace W0=dev_as_handle, W1=proc_handle, X2=dev_map_addr, X3=dev_as_size, X4=dev_as_addr

R0=dev_as_handle, R1=proc_handle, R2=dev_map_addr_lower32, R3=dev_map_addr_upper32, R4=dev_as_size, R5=dev_as_addr_lower32, R6=dev_as_addr_upper32

W0=result
0x5D svcInvalidateProcessDataCache W0=process_handle, X1=addr, X2=size

R0=process_handle, R1=size_lower32, R2=addr_lower32, R3=addr_upper32, R4=size_upper32

W0=size
0x5E svcStoreProcessDataCache W0=process_handle, X1=addr, X2=size

R0=process_handle, R1=size_lower32, R2=addr_lower32, R3=addr_upper32, R4=size_upper32

W0=size
0x5F svcFlushProcessDataCache W0=process_handle, X1=addr, X2=size

R0=process_handle, R1=size_lower32, R2=addr_lower32, R3=addr_upper32, R4=size_upper32

W0=size
0x60 svcDebugActiveProcess X1=pid

R2=pid_lower32, R3=pid_upper32

W0=result, W1=debug_handle
0x61 svcBreakDebugProcess W0=debug_handle W0=result
0x62 svcTerminateDebugProcess W0=debug_handle W0=result
0x63 svcGetDebugEvent X0=#DebugEventInfo*, W1=debug_handle W0=result
0x64 #svcContinueDebugEvent [1.0.0-2.3.0] W0=debug_handle, W1=#ContinueDebugFlagsOld, X2=thread_id

R0=debug_handle, R1=#ContinueDebugFlagsOld, R2=thread_id_lower32, R3=thread_id_upper32

[3.0.0+] W0=debug_handle, W1=#ContinueDebugFlags, X2=thread_id_list(u64 *), W3=num_tids (max 64, 0 means "all threads")

W0=result
0x65 #svcGetProcessList X1=pids_out_ptr, W2=max_out W0=result, W1=num_out
0x66 svcGetThreadList X1=tids_out_ptr, W2=max_out, W3=debug_handle_or_zero W0=result, X1=num_out
0x67 svcGetDebugThreadContext X0=ThreadContext*, X1=debug_handle, X2=thread_id, W3=#ThreadContextFlags

R0=ThreadContext*, R1=debug_handle, R2=thread_id_lower32, R3=thread_id_upper32, R4=#ThreadContextFlags

W0=result
0x68 svcSetDebugThreadContext W0=debug_handle, X1=thread_id, X2=ThreadContext*, W3=#ThreadContextFlags

R0=debug_handle, R1=ThreadContext*, R2=thread_id_lower32, R3=thread_id_upper32, R4=#ThreadContextFlags

W0=result
0x69 svcQueryDebugProcessMemory X0=#MemoryInfo*, X2=debug_handle, X3=addr W0=result, W1=PageInfo
0x6A svcReadDebugProcessMemory X0=buffer*, X1=debug_handle, X2=src_addr, X3=size W0=result
0x6B svcWriteDebugProcessMemory X0=debug_handle, X1=buffer*, X2=dst_addr, X3=size W0=result
0x6C #svcSetHardwareBreakPoint W0=HardwareBreakpointId, X1=watchpoint_flags/breakpoint_flags, X2=watchpoint_value/debug_handle

R0=HardwareBreakpointId, R1=value_lower32, R2=flags_lower32, R3=flags_upper32, R4=value_upper32

W0=result
0x6D svcGetDebugThreadParam X2=debug_handle, X3=thread_id, W4=#DebugThreadParam

R0=thread_id_lower32, R1=thread_id_upper32, R2=debug_handle, R3=#DebugThreadParam

W0=result, X1=out0, W2=out1

R0=result, R1=out0_lower32, R2=out0_upper32, R3=out1

0x6F [5.0.0+] #svcGetSystemInfo X1=info_id, X2=handle, X3=info_sub_id

R1=info_sub_id_lower32, R2=info_id, R3=handle, R4=info_sub_id_upper32

W0=result, X1=out

R0=result, R1=out_lower32, R2=out_upper32

0x70 svcCreatePort W2=max_sessions, W3=is_light, X4=name_ptr

R0=name_ptr, R2=max_sessions, R3=is_light

W0=result, W1=serverport_handle, W2=clientport_handle
0x71 svcManageNamedPort X1=name_ptr, W2=max_sessions W0=result, W1=serverport_handle
0x72 svcConnectToPort W1=clientport_handle W0=result, W1=session_handle
0x73 #svcSetProcessMemoryPermission W0=process_handle, X1=addr, X2=size, W3=perm

R0=process_handle, R1=size_lower32, R2=addr_lower32, R3=addr_upper32, R4=size_upper32, R5=perm

W0=result
0x74 #svcMapProcessMemory X0=dstaddr, W1=process_handle, X2=srcaddr, X3=size

R0=dstaddr, R1=process_handle, R2=srcaddr_lower32, R3=srcaddr_upper32, R4=size

W0=result
0x75 #svcUnmapProcessMemory X0=dstaddr, W1=process_handle, X2=srcaddr, X3=size

R0=dstaddr, R1=process_handle, R2=srcaddr_lower32, R3=srcaddr_upper32, R4=size

W0=result
0x76 #svcQueryProcessMemory X0=meminfo_ptr, W2=process_handle, X3=addr

R0=meminfo_ptr, R1=addr_lower32, R2=process_handle, R3=addr_upper32

W0=result, W1=pageinfo
0x77 #svcMapProcessCodeMemory W0=process_handle, X1=dstaddr, X2=srcaddr, X3=size

R0=process_handle, R1=srcaddr_lower32, R2=dstaddr_lower32, R3=dstaddr_upper32, R4=srcaddr_lower32, R5=size_lower32, R6=size_upper32

W0=result
0x78 #svcUnmapProcessCodeMemory W0=process_handle, X1=dstaddr, X2=srcaddr, X3=size

R0=process_handle, R1=srcaddr_lower32, R2=dstaddr_lower32, R3=dstaddr_upper32, R4=srcaddr_lower32, R5=size_lower32, R6=size_upper32

W0=result
0x79 #svcCreateProcess X1=procinfo_ptr, X2=caps_ptr, W3=cap_num W0=result, W1=process_handle
0x7A svcStartProcess W0=process_handle, W1=main_thread_prio, W2=default_cpuid, W3=main_thread_stacksz

R0=process_handle, R1=main_thread_prio, R2=default_cpuid, R3=main_thread_stacksz_lower32, R4=main_thread_stacksz_upper32

W0=result
0x7B svcTerminateProcess W0=process_handle W0=result
0x7C #svcGetProcessInfo W0=process_handle, W1=#ProcessInfoType

R1=process_handle, R2=#ProcessInfoType

W0=result, X1=#ProcessState

R0=result, R1=#ProcessState_lower32, R2=#ProcessState_upper32

0x7D svcCreateResourceLimit None W0=result, W1=reslimit_handle
0x7E svcSetResourceLimitLimitValue W0=reslimit_handle, W1=#LimitableResource, X2=value

R0=reslimit_handle, R1=#LimitableResource, R2=value_lower32, R3=value_upper32

W0=result
0x7F #svcCallSecureMonitor X0=smc_sub_id, X1,X2,X3,X4,X5,X6,X7=smc_args

R0=smc_sub_id, R1, R2, R3=smc_args

X0,X1,X2,X3,X4,X5,X6,X7=result

R0,R1,R2,R3=result

svcSetHeapSize

Argument Type Name
(In) W1 u64 Size
(Out) W0 #Result Ret
(Out) X1 u64 OutAddr

Description: Set the process heap to a given Size. It can both extend and shrink the heap.

Size must be a multiple of 0x200000 (2MB).

On success, the heap base-address (which is fixed by kernel, aslr'd, and always in the Heap memory region) is written to OutAddr.

Uses current process pool partition. The memory allocated counts towards the caller's process Memory ResourceLimit.

[2.0.0+] Size must be less than or equal to 4GB.

Result codes

0x0: Success.

0xCA01: Invalid size passed. It's either bigger than 4GB, or misaligned.

0xD001: Size is bigger than the Heap Region size.

0xCE01: KMemoryBlockAllocator slab allocator exhausted.

0xD401: The memory region is in an invalid state. Likely because a mapping was made in the heap region.

0x10801: Memory resource limit reached.

svcSetMemoryPermission

Argument Type Name
(In) X0 void* Addr
(In) X1 u64 Size
(In) W2 #Permission Prot
(Out) W0 #Result Ret

Description: Change permission of page-aligned memory region.

Bit2 of permission (exec) is not allowed. Setting write-only is not allowed either (bit1).

This can be used to move back and forth between ---, r-- and rw-.

Result codes

0x0: Success. The memory region was reprotected.

0xCC01: Unaligned address specified.

0xCA01: Unaligned or zero size specified.

0xD401: The provided memory region does not fall within the userland address space.

0xD801: Invalid permission specified. Valid permissions are ---, r-- and rw-.

0xD401: The provided memory region was in an invalid state. The region must have the PermissionChangeAllowed bit set in its #MemoryState, and must not have the IsBorrowed or IsUncached #MemoryAttribute.

0xCE01: Kernel resource exhausted.

svcSetMemoryAttribute

Argument Type Name
(In) X0 void* Addr
(In) X1 u64 Size
(In) W2 u32 State0
(In) W3 u32 State1
(Out) W0 #Result Ret

Description: Change attribute of page-aligned memory region.

This is used to turn on/off caching for a given memory area. Useful when talking to devices such as the GPU.

What happens "under the hood" is the "Memory Attribute Indirection Register" index is changed from 2 to 3 in the MMU descriptor.

State0 State1 Action
0 0 Clear bit3 in #MemoryAttribute.
8 0 Clear bit3 in #MemoryAttribute.
8 8 Set bit3 in #MemoryAttribute.

svcMapMemory

Argument Type Name
(In) X0 void* DstAddr
(In) X1 void* SrcAddr
(In) X2 u64 Size
(Out) W0 #Result Ret

Description: Maps a memory range into a different range.

Mainly used for adding guard pages around stack.

Source range gets reprotected to --- (it can no longer be accessed), and bit0 is set in the source #MemoryAttribute.

[1.0.0] This could be used to map into either the Alias Region or the Stack region.

[2.0.0+] This can only be used to map into the Stack region.

Code can get the range of the Alias region from #svcGetInfo id0=2,3, and on 2.0.0+ the range of the Stack region via #svcGetInfo id0=14, 15 (on 1.0.0, the Stack region had hardcoded limits).

When mapped into the Alias region, the mapped memory will have state 0x482907.

When mapped into the Stack region, the mapped memory will have state 0x5C3C0B.

svcUnmapMemory

Argument Type Name
(In) X0 void* DstAddr
(In) X1 void* SrcAddr
(In) X2 u64 Size
(Out) W0 #Result Ret

Description: Unmaps a region that was previously mapped with #svcMapMemory.

It's possible to unmap ranges partially, you don't need to unmap the entire range "in one go".

The srcaddr/dstaddr must match what was given when the pages were originally mapped.

svcQueryMemory

Argument Type Name
(In) X0 #MemoryInfo* MemInfo
(In) X2 void* Addr
(Out) W0 #Result Ret
(Out) W1 PageInfo PageInfo

Description: Query information about an address. Will always fetch the lowest page-aligned mapping that contains the provided address.

Outputs a #MemoryInfo struct.

svcExitProcess

Argument Type Name
(In) None
(Out) None

Description: Exits the current process.

svcCreateThread

Argument64 Argument32 Type Name
(In) X1 R1 void(*)(void*) Entry
(In) X2 R2 void* ThreadContext
(In) X3 R3 void* StackTop
(In) W4 R0 u32 Priority
(In) W5 R4 u32 ProcessorId
(Out) W0 R0 #Result Ret
(Out) W1 R1 Handle<Thread> Handle

Description: Create a thread in the current process.

Processor_id must be 0,1,2,3 or -2, where -2 uses the default cpuid for process.

svcStartThread

Argument Type Name
(In) W0 Handle<Thread> Handle
(Out) None

Description: Starts the thread for the provided handle.

svcExitThread

Argument Type Name
(In) None
(Out) None

Description: Exits the current thread.

svcSleepThread

Argument64 Argument32 Type Name
(In) X0 R0, R1 s64 Nanoseconds

Description: Sleep for a specified amount of time, or yield thread.

Setting nanoseconds to 0, -1, or -2 indicates a yielding type.

Value Type
0 Yielding without core migration
-1 Yielding with core migration
-2 Yielding to any other thread

svcGetThreadPriority

Argument Type Name
(In) W1 Handle<Thread> Handle
(Out) W0 #Result Ret
(Out) W1 u64 Priority

Description: Get priority of provided thread handle.

svcSetThreadPriority

Argument Type Name
(In) W0 Handle<Thread> Handle
(In) W1 u32 Priority
(Out) W0 #Result Ret

Description: Set priority of provided thread handle.

Priority is a number 0-0x3F. Lower value means higher priority.

svcGetThreadCoreMask

Argument64 Argument32 Type Name
(In) W2 R2 Handle<Thread> Handle
(Out) W0 R0 #Result Ret
(Out) W1 R1 u32 Out0
(Out) X2 R2, R3 u64 Out1

Description: Get affinity mask of provided thread handle.

svcSetThreadCoreMask

Argument64 Argument32 Type Name
(In) W0 R0 Handle<Thread> Handle
(In) W1 R1 u32 In0
(In) X2 R2, R3 u64 In1
(Out) W0 R0 #Result Ret

Description: Set affinity mask of provided thread handle.

svcGetCurrentProcessorNumber

Argument Type Name
(In) None
(Out) W0/X0 u64 CpuId

Description: Get which cpu is executing the current thread.

Cpu-id is an integer in the range 0-3.

svcSignalEvent

Argument Type Name
(In) W0 Handle<WritableEvent> Event
(Out) X0 #Result Result

Description: Puts the given event in the signaled state.

Will wake up any thread currently waiting on this event. Can potentially trigger a reschedule.

Any calls to #svcWaitSynchronization on this handle will return immediately, until the event's signaled state is reset.

Result codes

0x0: Success. Event is now in signaled state.

0xE401: Invalid handle. The handle either does not exist, or is not a WritableEvent.

svcClearEvent

Argument Type Name
(In) W0 Handle<WritableEvent or ReadableEvent> Event
(Out) X0 #Result Result

Description: Takes the given event out of the signaled state.

Result codes

0x0: Success, the event is now in the not-signaled state.

0xE401: Invalid handle. The handle either does not exist, or is not a ReadableEvent nor a WritableEvent.

0xFA01: The handle was not in a signaled state.

svcMapSharedMemory

Argument Type Name
(In) W0 Handle<SharedMemory> MemHandle
(In) X1 void* Addr
(In) X2 u64 Size
(In) W3 #Permission Permissions
(Out) W0 #Result Ret

Maps the block supplied by the handle. The required permissions are different for the process that created the handle and all other processes.

Increases reference count for the KSharedMemory object. Thus in order to release the memory associated with the object, all handles to it must be closed and all mappings must be unmapped.

svcCreateTransferMemory

Argument Type Name
(In) X1 void* Addr
(In) X2 u64 Size
(In) W3 #Permission Permissions
(Out) W0 #Result Ret
(Out) W1 Handle<TransferMemory> Handle

This one reprotects the src block with perms you give it. It also sets bit0 into #MemoryAttribute.

Executable bit perm not allowed.

Closing all handles automatically causes the bit0 in #MemoryAttribute to clear, and the permission to reset.

svcResetSignal

Argument Type Name
(In) W0 Handle<ReadableEvent> or Handle<Process> Handle
(Out) W0 #Result Ret

Resets the signal on the given handle, ensuring future calls to #svcWaitSynchronization on this handle will sleep until the handle is signaled again. If the handle is a ReadableEvent, this is equivalent to calling svcClearEvent() on the handle.

If the handle is a Process, it will clear the signaled state (which is set when the process changes #ProcessState. Once the process enters the Exited state, calling svcResetSignal on the process will no longer have an effect (the process is permanently signaled), and the syscall will return 0xFA01.

Result codes

0x0: Success. The signal was reset.

0xE401: The handle is invalid or of the wrong type.

0xFA01: The handle was not signaled, or the process is in exited state, causing it to be permanently signaled.

svcWaitSynchronization

Argument64 Argument32 Type Name
(In) X1 R1 Handle* HandlesPtr
(In) W2 R2 u64 HandlesNum
(In) X3 R0, R3 u64 Timeout
(Out) W0 R0 #Result Ret
(Out) W1 R1 u64 HandleIndex

Works with num_handles <= 0x40.

When zero handles are passed, this will wait forever until either timeout or cancellation occurs.

Does not accept 0xFFFF8001 or 0xFFFF8000 as handles.

Object types

KDebug: signals when there is a new DebugEvent (retrievable via #svcGetDebugEvent).

KClientPort: signals when the number of sessions is less than the maximum allowed.

KProcess: signals when the process undergoes a state change (retrievable via #svcGetProcessInfo).

KReadableEvent: signals when the event's corresponding KWritableEvent has been signaled via svcSignalEvent.

KServerPort: signals when there is an incoming connection waiting to be accepted.

KServerSession: signals when there is an incoming message waiting to be received or the pipe is closed.

KThread: signals when the thread has exited.

Result codes

0x0: Success. One of the objects was signaled before the timeout expired, or one of the objects is a Session with a closed remote. Handle index is updated to indicate which object signaled.

0x7601: Thread termination requested. Handle index is not updated.

0xe401: Invalid handle. Returned when one of the handles passed is invalid. Handle index is not updated.

0xe601: Invalid address. Returned when the handles pointer is not a readable address. Handle index is not updated.

0xea01: Timeout. Returned when no objects have been signaled within the timeout. Handle index is not updated.

0xec01: Interrupted. Returned when another thread uses #svcCancelSynchronization to cancel this thread. Handle index is not updated.

0xee01: Too many handles. Returned when the number of handles passed is > 0x40.

svcCancelSynchronization

Argument Type Name
(In) W0 Handle<Thread> Handle
(Out) W0 #Result Ret

If the referenced thread is currently in a synchronization call (#svcWaitSynchronization, #svcReplyAndReceive or #svcReplyAndReceiveLight), that call will be interrupted and return 0xec01. If that thread is not currently executing such a synchronization call, the next call to a synchronization call will return 0xec01.

This doesn't take force-pause (activity/debug pause) into account.

Result codes

0x0: Success. The thread was either interrupted or has had its flag set.

0xe401: Invalid handle. The handle given was either invalid or not a thread handle.

svcGetSystemTick

Argument64 Argument32 Type Name
(Out) X0 R0, R1 u64 Ticks

Returns the value of cntpct_el0.

The frequency is 19200000 Hz (constant from official sw).

Official sw reads cntpct_el0 directly from usermode without using this SVC. sdk-nso has this SVC, but it's not known to be called anywhere.

svcSendSyncRequestWithUserBuffer

Argument Type Name
(In) X0 void* CmdPtr
(In) X1 u64 Size
(In) W2 Handle<Session> Handle
(Out) W0 #Result Ret

Size and CmdPtr must be 0x1000-aligned.

Result codes

0x0: Success.

0xcc01: CmdPtr is not 0x1000-aligned.

0xca01: Size is not 0x1000-aligned.

0xce01: KSessionRequest allocation failed (unlikely) or pointer buffer size exceeded.

0xe401: Handles does not exist, or handle is not an instance of KClientSession.

svcBreak

Argument Type Name
(In) X0 u64 Break Reason
(In) X1 u64
(In) X2 u64 Info
(Out) W0 Result 0 (Success)

If the process is attached, report the Break event. Then, if svcContinueDebugEvent didn't apply IgnoreException on the thread: if TPIDR_EL0 is 0, adjust ELR_EL1 to retry to svc instruction (and set TPIDR_EL0 to 1).

Otherwise, if bit31 in reason isn't set, perform crash reporting (see Exception Handling section below), if it doesn't terminate the process adjust ELR_EL1 as well.

Otherwise just return 0.

svcGetInfo

Argument Type Name
(In) W1 u32 InfoId
(In) W2 Handle Handle
(In) X3 u64 InfoSubId
(Out) W0 #Result Ret
(Out) X1 u64 Out
Argument Type Name
(In) R0 u32 InfoSubIdLower32
(In) R1 u32 InfoId
(In) R2 Handle Handle
(In) R3 u32 InfoSubIdUpper32
(Out) R0 #Result Ret
(Out) R1 u32 OutLower32
(Out) R2 u32 OutUpper32


Handle type Id0 Id1 Description
Process 0 0 AllowedCpuIdBitmask
Process 1 0 AllowedThreadPrioBitmask
Process 2 0 AliasRegionBaseAddr
Process 3 0 AliasRegionSize
Process 4 0 HeapRegionBaseAddr
Process 5 0 HeapRegionSize
Process 6 0 TotalMemoryAvailable. Total memory available(free+used).
Process 7 0 TotalMemoryUsage. Total used size of codebin memory + main-thread stack + allocated heap.
Zero 8 0 IsCurrentProcessBeingDebugged
Zero 9 0 Returns ResourceLimit handle for current process. Used by PM.
Zero 10 -1, {current coreid} IdleTickCount
Zero 11 0-3 RandomEntropy from current process. TRNG. Used to seed usermode PRNGs.
Process 12 0 [2.0.0+] AddressSpaceBaseAddr
Process 13 0 [2.0.0+] AddressSpaceSize
Process 14 0 [2.0.0+] StackRegionBaseAddr
Process 15 0 [2.0.0+] StackRegionSize
Process 16 0 [3.0.0+] PersonalMmHeapSize
Process 17 0 [3.0.0+] PersonalMmHeapUsage
Process 18 0 [3.0.0+] TitleId
Zero 19 0 [4.0.0-4.1.0] PrivilegedProcessId_LowerBound
Zero 19 1 [4.0.0-4.1.0] PrivilegedProcessId_UpperBound
Process 20 0 [5.0.0+] UserExceptionContextAddr
Process 21 0 [6.0.0+] TotalMemoryAvailableWithoutMmHeap
Process 22 0 [6.0.0+] TotalMemoryUsedWithoutMmHeap
Process 23 0 [9.0.0+] IsApplication
Thread 0xF0000002 0-3, -1 Thread Ticks. When 0-3 are passed, gets specific core CPU ticks spent on thread. When -1 is passed, gets total CPU ticks spent on thread.

svcMapPhysicalMemory

This is like svcSetHeapSize except you can allocate heap at any address you'd like.

Uses current process pool partition.

svcDumpInfo

Argument Type Name
(In) None
(Out) None

Does nothing, just returns with registers set to all-zero.

svcAcceptSession

Argument Type Name
(In) W1 Handle<Port> Port
(Out) W0 #Result Result
(Out) W1 Handle<ServerSession> Session

Result codes

0xf201: No session waiting to be accepted

svcReplyAndReceive

Argument64 Argument32 Type Name
(In) W1 R1 *Handle<Port or ServerSession> Handles
(In) W2 R2 u32 NumHandles
(In) W3 R3 Handle<ServerSession> ReplyTarget
(In) X4 R0, R4 u64 (nanoseconds) Timeout
(Out) W0 R0 #Result Result
(Out) W1 R1 u32 HandleIndex

If ReplyTarget is not zero, a reply from the TLS will be sent to that session. Then it will wait until either of the passed sessions has an incoming message, is closed, a passed port has an incoming connection, or the timeout expires. If there is an incoming message, it is copied to the TLS.

If ReplyTarget is zero, the TLS should contain a blank message. If this message has a C descriptor, the buffer it points to will be used as the pointer buffer. See IPC_Marshalling#IPC_buffers. Note that a pointer buffer cannot be specified if ReplyTarget is not zero.

After being validated, passed handles will be enumerated in order; even if a session has been closed, if one that appears earlier in the list has an incoming message, it will take priority and a result code of 0x0 will be returned.

Result codes

0x0: Success. Either a session has an incoming message or a port has an incoming connection. HandleIndex is set appropriately.

0xea01: Timeout. No handles were signalled before the timeout expired. HandleIndex is not updated.

0xf601: Port remote dead. One of the sessions has been closed. HandleIndex is set appropriately.

svcMapPhysicalMemoryUnsafe

Same as #svcMapPhysicalMemory except it always uses pool partition 0.

svcCreateCodeMemory

Takes an address range with backing memory to create the code memory object.

The memory is initially memset to 0xFF after being locked.

svcControlCodeMemory

Maps the backing memory for a Code memory object into the current process.

For CodeMemoryOperation_MapOwner, memory permission must be RW-.

For CodeMemoryOperation_MapSlave, memory permission must be R-- or R-X.

Operations CodeMemoryOperation_UnmapOwner/CodeMemoryOperation_UnmapSlave unmap memory that was previously mapped this way.

This allows one "secure JIT" process to map the code memory as RW-, and the other "slave" process to map it R-X.

[5.0.0+] Error 0xE401 is now returned when the process owner of the Code memory object is the same as the current process.

svcReadWriteRegister

Argument64 Argument32 Type Name
(In) X1 R2, R3 u64 RegAddr
(In) W2 R0 u64 RwMask
(In) W3 R1 u64 InValue
(Out) W0 R0 #Result Ret
(Out) W1 R1 u64 OutValue

Read/write IO registers with a hardcoded whitelist. Input address is physical-address and must be aligned to 4.

rw_mask is 0 for reading and 0xffffffff for writing. You can also write individual bits by using a mask value.

You can only write to registers inside physical pages 0x70019000 (MC), 0x7001C000 (MC0), 0x7001D000 (MC1), and they all share the same whitelist.

The whitelist is same for writing as for reading.

The whitelist is:

0x054, 0x090, 0x094, 0x098, 0x09c, 0x0a0, 0x0a4, 0x0a8, 0x0ac, 0x0b0, 0x0b4, 0x0b8, 0x0bc, 0x0c0, 0x0c4, 0x0c8, 0x0d0, 0x0d4, 0x0d8, 0x0dc, 0x0e0, 0x100, 0x108, 0x10c, 0x118, 0x11c, 0x124, 0x128, 0x12c, 0x130, 0x134, 0x138, 0x13c, 0x158, 0x15c, 0x164, 0x168, 0x16c, 0x170, 0x174, 0x178, 0x17c, 0x200, 0x204, 0x2e4, 0x2e8, 0x2ec, 0x2f4, 0x2f8, 0x310, 0x314, 0x320, 0x328, 0x344, 0x348, 0x370, 0x374, 0x37c, 0x380, 0x390, 0x394, 0x398, 0x3ac, 0x3b8, 0x3bc, 0x3c0, 0x3c4, 0x3d8, 0x3e8, 0x41c, 0x420, 0x424, 0x428, 0x42c, 0x430, 0x44c, 0x47c, 0x480, 0x484, 0x50c, 0x554, 0x558, 0x55c, 0x670, 0x674, 0x690, 0x694, 0x698, 0x69c, 0x6a0, 0x6a4, 0x6c0, 0x6c4, 0x6f0, 0x6f4, 0x960, 0x970, 0x974, 0xa20, 0xa24, 0xb88, 0xb8c, 0xbc4, 0xbc8, 0xbcc, 0xbd0, 0xbd4, 0xbd8, 0xbdc, 0xbe0, 0xbe4, 0xbe8, 0xbec, 0xc00, 0xc5c, 0xcac


[2.0.0+] Whitelist was extended with 0x4c4, 0x4c8, 0x4cc, 0x584, 0x588, 0x58c.

[2.0.0+] The IO registers in range 0x7000E400 (PMC) size 0xC00 skip the whitelist, and do a TrustZone call using SMC Id1 0xC3000008(ReadWriteRegister).

[4.0.0+] Access to the Memory Controller (0x70019000) also uses smcReadWriteRegister.

Here is the whitelist imposed by that SMC, relative to the start of the PMC registers:

0x000, 0x00c, 0x010, 0x014, 0x01c, 0x020, 0x02c, 0x030, 0x034, 0x038, 0x03c, 0x040, 0x044, 0x048, 0x0dc, 0x0e0, 0x0e4, 0x160, 0x164, 0x168, 0x170, 0x1a8, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x2b4, 0x2d4, 0x440, 0x4d8

Here is the whitelist imposed by smcReadWriteRegister (checked in addition to the whitelist in svcReadWriteRegister), relative to the start of the MC registers:

0x000, 0x004, 0x008, 0x00C, 0x010, 0x01C, 0x020, 0x030, 0x034, 0x050, 0x054, 0x090, 0x094, 0x098, 0x09C, 0x0A0, 0x0A4, 0x0A8, 0x0AC, 0x0B0, 0x0B4, 0x0B8, 0x0BC, 0x0C0, 0x0C4, 0x0C8, 0x0D0, 0x0D4, 0x0D8, 0x0DC, 0x0E0, 0x100, 0x108, 0x10C, 0x118, 0x11C, 0x124, 0x128, 0x12C, 0x130, 0x134, 0x138, 0x13C, 0x158, 0x15C, 0x164, 0x168, 0x16C, 0x170, 0x174, 0x178, 0x17C, 0x200, 0x204, 0x238, 0x240, 0x244, 0x250, 0x254, 0x258, 0x264, 0x268, 0x26C, 0x270, 0x274, 0x280, 0x284, 0x288, 0x28C, 0x294, 0x2E4, 0x2E8, 0x2EC, 0x2F4, 0x2F8, 0x310, 0x314, 0x320, 0x328, 0x344, 0x348, 0x370, 0x374, 0x37C, 0x380, 0x390, 0x394, 0x398, 0x3AC, 0x3B8, 0x3BC, 0x3C0, 0x3C4, 0x3D8, 0x3E8, 0x41C, 0x420, 0x424, 0x428, 0x42C, 0x430, 0x44C, 0x47C, 0x480, 0x484, 0x4C4, 0x4C8, 0x4CC, 0x50C, 0x554, 0x558, 0x55C, 0x584, 0x588, 0x58C, 0x670, 0x674, 0x690, 0x694, 0x698, 0x69C, 0x6A0, 0x6A4, 0x6C0, 0x6C4, 0x6F0, 0x6F4, 0x960, 0x970, 0x974, 0x9B8, 0xA20, 0xA24, 0xA88, 0xA94, 0xA98, 0xA9C, 0xAA0, 0xAA4, 0xAA8, 0xAAC, 0xAB0, 0xAB4, 0xAB8, 0xABC, 0xAC0, 0xAC4, 0xAC8, 0xACC, 0xAD0, 0xAD4, 0xAD8, 0xADC, 0xAE0, 0xB88, 0xB8C, 0xBC4, 0xBC8, 0xBCC, 0xBD0, 0xBD4, 0xBD8, 0xBDC, 0xBE0, 0xBE4, 0xBE8, 0xBEC, 0xC00, 0xC5C, 0xCAC

svcCreateSharedMemory

Argument Type Name
(In) W1 u64 Size
(In) W2 #Permission LocalPerm
(In) W3 #Permission RemotePerm
(Out) W0 #Result Ret
(Out) W1 Handle<SharedMemory> MemHandle

Other perm can be used to enforce permission 1, 3, or 0x10000000 if don't care.

Allocates memory from the current process' pool partition.

svcMapTransferMemory

Argument Type Name
(In) X0 Handle<TransferMemory> MemHandle
(In) X1 void* Addr
(In) X2 u64 Size
(In) W3 #Permission Permissions
(Out) W0 #Result Ret

The newly mapped pages will have #MemoryState type 0xE.

You must pass same size and permissions as given in svcCreateMemoryMirror, otherwise error.

svcUnmapTransferMemory

Argument Type Name
(In) X0 Handle<TransferMemory> MemHandle
(In) X1 void* Addr
(In) X2 u64 Size
(Out) W0 #Result Ret

Size must match size given in map syscall, otherwise there's an invalid-size error.


svcCreateInterruptEvent

Argument Type Name
(In) X1 u64 IrqNum
(In) W2 bool Flags
(Out) W0 #Result Ret
(Out) W1 Handle<ReadableEvent> ReadableEventHandle

Create an event handle for the given IRQ number. Waiting on this handle will wait until the IRQ is triggered. The flags argument configures the triggering. If it is false, the IRQ is active HIGH level sensitive, if it is true it is rising-edge sensitive.

Result codes

0x0: Success.

0xF001: Flags was > 1

0xF201: IRQ above 0x3FF or outside the IRQ access mask was given.

0xCE01: A SlabHeap was exhausted (too many interrupts created).

0xF401: IRQ already has an event registered.

0xD201: The handle table is full. Try closing some handles.


svcQueryPhysicalAddress

Argument Type Name
(In) X1 u64 Addr
(Out) W0 #Result Ret
(Out) X1 u64 PhysAddr
(Out) X2 u64 BaseAddr
(Out) X3 u64 Size

Description: Query the physical address of a virtual address. Will always fetch the lowest page-aligned mapping that contains the provided physical address.

The returned BaseAddr is the virtual address of that page-aligned mapping, while PhysAddr is the physical address of that page. Size is the amount of continuous physical memory in that mapping.

svcQueryIoMapping

Argument64 Argument32 Type Name
(In) X1 R2, R3 u64 PhysAddr
(In) X2 R0 u64 Size
(Out) W0 R0 #Result Ret
(Out) X1 R1 void* VirtAddr

Description: Returns a virtual address mapped to a given IO range.

svcCreateDeviceAddressSpace

Argument64 Argument32 Type Name
(In) X1 R2, R3 u64 StartAddr
(In) X2 R0, R1 u64 EndAddr
(Out) W0 R0 #Result Ret
(Out) W1 R1 Handle<DeviceAddressSpace> AddressSpaceHandle

Description: Creates a virtual address space for binding device address spaces and returns a handle.

dev_as_start_addr is normally set to 0 and dev_as_end_addr is normally set to 0xFFFFFFFF.

svcAttachDeviceAddressSpace

Argument Type Name
(In) W0 #DeviceName DeviceId
(In) X1 Handle<DeviceAddressSpace> DeviceAsHandle
(Out) W0 #Result Ret

Description: Attaches a device address space to a device.

svcDetachDeviceAddressSpace

Argument Type Name
(In) W0 #DeviceName DeviceId
(In) X1 Handle<DeviceAddressSpace> DeviceAsHandle
(Out) W0 #Result Ret

Description: Detaches a device address space from a device.

svcMapDeviceAddressSpaceByForce

Argument64 Argument32 Type Name
(In) W0 R0 Handle<DeviceAddressSpace> DeviceAsHandle
(In) W1 R1 Handle<Process> ProcessHandle
(In) X2 R2, R3 void* SrcAddr
(In) X3 R4 u64 DeviceAsSize
(In) X4 R5, R6 u64 DeviceAsAddr
(In) W5 R7 #Permission Permissions
(Out) W0 R0 #Result Ret

Description: Maps an attached device address space to an userspace address.

dev_map_addr is the userspace destination address, while dev_as_addr is the source address between dev_as_start_addr and dev_as_end_addr (passed to #svcCreateDeviceAddressSpace).

The userspace destination address must have the MapDeviceAllowed bit set. Bit IsDeviceMapped will be set after mapping.

svcMapDeviceAddressSpaceAligned

Argument64 Argument32 Type Name
(In) W0 R0 Handle<DeviceAddressSpace> DeviceAsHandle
(In) W1 R1 Handle<Process> ProcessHandle
(In) X2 R2, R3 void* SrcAddr
(In) X3 R4 u64 DeviceAsSize
(In) X4 R5, R6 u64 DeviceAsAddr
(In) W5 R7 #Permission Permissions
(Out) W0 R0 #Result Ret

Description: Maps an attached device address space to an userspace address.

Same as #svcMapDeviceAddressSpaceByForce, but the userspace destination address must have the MapDeviceAlignedAllowed bit set instead.

svcUnmapDeviceAddressSpace

Argument64 Argument32 Type Name
(In) W0 R0 Handle<DeviceAddressSpace> DeviceAsHandle
(In) W1 R1 Handle<Process> ProcessHandle
(In) X2 R2, R3 void* SrcAddr
(In) X3 R4 u64 DeviceAsSize
(In) X4 R5, R6 u64 DeviceAsAddr
(Out) W0 R0 #Result Ret

Description: Unmaps an attached device address space from an userspace address.

svcContinueDebugEvent

[1.0.0-2.3.0] Argument64 Argument32 Type Name
(In) W0 R0 Handle<Debug> DebugHandle
(In) W1 R1 #ContinueDebugFlagsOld DebugFlags
(In) X2 R2, R3 u64 ThreadId
(Out) W0 R0 #Result Ret
[3.0.0+] Argument64 Argument32 Type Name
(In) W0 R0 Handle<Debug> DebugHandle
(In) W1 R1 #ContinueDebugFlags DebugFlags
(In) X2 R2 u64* ThreadIdList
(In) X3 R3 size_t NumTids (max 64 - 0 means "all threads")
(Out) W0 R0 #Result Ret

Result codes

0x0: Success. The process has been resumed.

0xe401: Invalid debug handle.

0xf401: Process has debug events queued or is already running.

svcGetProcessList

Argument64 Argument32 Type Name
(In) X1 R1 u64* PidBuffer
(In) X2 R2 size_t PidBufferSize
(Out) X0 R0 #Result Ret
(Out) X1 R1 size_t NumProcesses

Description: Fills the provided array with the pids of currently living processes. A process "lives" so long as it is currently running or a handle to it still exists.

It returns the total number of processes currently alive. If this number is bigger than the size of PidBuffer, the user won't have all the pids.

Result codes

0x0: Success.

0xd401: The provided buffer is outside the process address space.

0xe601: copyToUser failed. The provided buffer is not user-accessible.

0xee01: The provided buffer size is too big. Max value is 0xFFFFFFF.

svcGetSystemInfo

Argument Type Name
(In) X1 u64 InfoId
(In) W2 Handle Handle
(In) X3 u64 InfoSubId
(Out) W0 #Result Ret
(Out) X1 u64 Out
Handle type Id0 Id1 Description
Zero 0 0 TotalMemorySize_Application
Zero 0 1 TotalMemorySize_Applet
Zero 0 2 TotalMemorySize_System
Zero 0 3 TotalMemorySize_SystemUnsafe
Zero 1 0 CurrentMemorySize_Application
Zero 1 1 CurrentMemorySize_Applet
Zero 1 2 CurrentMemorySize_System
Zero 1 3 CurrentMemorySize_SystemUnsafe
Zero 2 0 PrivilegedProcessId_LowerBound
Zero 2 1 PrivilegedProcessId_UpperBound

svcSetProcessMemoryPermission

Argument64 Argument32 Type Name
(In) W0 R0 Handle<Process> ProcessHandle
(In) X1 R2, R3 u64 Addr
(In) X2 R1, R4 u64 Size
(In) W3 R5 void* Perm
(Out) W0 R0 #Result Ret

This sets the memory permissions for the specified memory with the supplied process handle.

This throws an error(0xD801) when the input perm is >0x5, hence -WX and RWX are not allowed.

svcMapProcessMemory

Argument64 Argument32 Type Name
(In) X0 R0 u64 DstAddr
(In) W1 R1 Handle<Process> ProcessHandle
(In) X2 R2, R3 void* SrcAddr
(In) X3 R4 u64 Size
(Out) W0 R0 #Result Ret

Maps the src address from the supplied process handle into the current process.

This allows mapping code and rodata with RW- permission.

svcUnmapProcessMemory

Argument64 Argument32 Type Name
(In) X0 R0 void* DstAddr
(In) W1 R1 Handle<Process> ProcessHandle
(In) X2 R2, R3 u64 SrcAddr
(In) X3 R4 u64 Size
(Out) W0 R0 #Result Ret

Unmaps what was mapped by #svcMapProcessMemory.

svcQueryProcessMemory

Argument64 Argument32 Type Name
(In) X0 R0 #MemoryInfo* MemInfoPtr
(In) W2 R2 Handle<Process> ProcessHandle
(In) X3 R1, R3 u64 Addr
(Out) W0 R0 #Result Ret
(Out) W1 R1 PageInfo PageInfo

Equivalent to #svcQueryMemory except takes a process handle.

svcMapProcessCodeMemory

Argument64 Argument32 Type Name
(In) W0 R0 Handle<Process> ProcessHandle
(In) X1 R2, R3 u64 DstAddr
(In) X2 R1, R4 u64 SrcAddr
(In) X3 R5, R6 u64 Size
(Out) W0 R0 #Result Ret

Takes a process handle, and maps normal heap in that process as executable code in that process. Used when loading NROs. This does not support using the current-process handle alias.

svcUnmapProcessCodeMemory

Argument64 Argument32 Type Name
(In) W0 R0 Handle<Process> ProcessHandle
(In) X1 R2, R3 u64 DstAddr
(In) X2 R1, R4 u64 SrcAddr
(In) X3 R5, R6 u64 Size
(Out) W0 R0 #Result Ret

Unmaps what was mapped by #svcMapProcessCodeMemory.

svcCreateProcess

Argument Type Name
(In) X1 #CreateProcessInfo* InfoPtr
(In) X2 u32* CapabilitiesPtr
(In) X3 u64 CapabilitiesNum
(Out) W0 #Result Ret
(Out) W1 Handle<Process> ProcessHandle

Takes a #CreateProcessInfo as input. CapabilitiesPtr points to an array of kernel capabilities. CapabilitiesNum is a number of capabilities in the CapabilitiesPtr array (number of element, not number of bytes).

Result codes

0x0: Success.

0xCA01: Attempted to map more code pages than available in address space.

0xCC01: Provided CodeAddr is invalid (make sure it's in range?)

0xE401: The resource handle passed is invalid.

0xE601: Attempt to copy procinfo from user-supplied pointer failed. Attempt to copy capabilities_num from user-supplied pointer failed.

0xE801: Attempted to create a 32-bit process with a 36-bit address space.

0xF001: Unused bits are set in mmuflags. Unknown address space type used.

svcGetProcessInfo

Argument64 Argument32 Type Name
(In) W0 R1 Handle<Process> ProcessHandle
(In) W1 R2 #ProcessInfoType InfoType
(Out) W0 R0 #Result Ret
(Out) X1 R1, R2 #ProcessState State

Returns an enum with value 0-7.

svcCallSecureMonitor

Argument64 Argument32 Type Name
(In) X0 R0 u64 Function ID
(In) X1-X7 R1-R3 u64 SMC sub-arguments
(Out) X0 R0 SMC Result Result of SMC
(Out) X1-X7 R1-R3 u64 SMC sub-output

Takes in a SMC function ID in X0, and arguments for that SMC function in X1-X7.

Passing an invalid SMC function ID or calling from a core other than core 3 will result in a secure monitor panic.

The kernel parses bits 9-15 in the passed SMC function ID (per the ARM SMC calling convention), and when set uses as an indicator to translate a pointer in the associated register (X1-X7) to a physical address. The kernel will translate any address mapped as R-W, other addresses (R--, R-X, or invalid pointers) will be translated as 0/NULL.

Output is returned raw from the Secure Monitor; X0 will be the untranslated SMC result and X1-X7 will contain other SMC output (or be unchanged, depending on the SMC).

Debugging

[2.0.0+] Exactly 6 debug SVCs require that IsDebugMode is non-zero. Error 0x4201 is returned otherwise.

  • svcBreakDebugProcess
  • svcContinueDebugEvent
  • svcWriteDebugProcessMemory
  • svcSetDebugThreadContext
  • svcTerminateDebugProcess
  • svcSetHardwareBreakPoint

svcDebugActiveProcess stops execution of the target process, the normal method for resuming it requires svcContinueDebugEvent(see above). Closing the debug handle also results in execution being resumed.

svcSetHardwareBreakPoint

Argument64 Argument32 Type Name
(In) W0 R0 u32 hardware_breakpoint_id
(In) X1 R2, R3 u64 flags
(In) X2 R1, R4 u64 value
(Out) W0 R0 #Result Ret

Sets one of the AArch64 hardware breakpoints. The nintendo switch has 6 hardware breakpoints, and 4 hardware watchpoints. The syscall has two behaviors depending on the value of hardware_breakpoint_id:

If hardware_breakpoint_id < 0x10, then it sets one of the AArch64 hardware breakpoints. Flags will go to DBGBCRn_EL1, and value to DBGBVRn_EL1. The only flags the user is allowed to set are those in the bitmask 0x7F01E1. Furthermore, the kernel will or it with 0x4004, in order to set various security flags to guarantee the watchpoints only triggers for code in EL0. If the user asks for a Breakpoint Type of ContextIDR match, the kernel shall use the given debug_handle to set DBGBVRn_EL1 to the ContextID of the debugged process.


If hardware_breakpoint_id is between 0x10 and 0x20 (exclusive), then it sets one of the AArch64 hardware watchpoints. Flags will go to DBGWCRn_EL1, and the value to DBGWVRn_EL1. The only flags the user is allowed to set are those in the bitmask 0xFF0F1FF9. Furthermore, the kernel will or it with 0x104004. This will set various security flags, and set the watchpoint type to be a Linked Watchpoint. This means that you need to link it to a Linked ContextIDR breakpoint. Check the ARM documentation for more information.

Note that hardware_breakpoint_id 0 to 4 match only to Virtual Address, while hardware_breakpoint_id 5 and 6 match against either Virtual Address, ContextID, or VMID. As such, if you are configuring a breakpoint to link for a watchpoint, make sure you use hardware_breakpoint_id 5 or 6.


For more documentation for hardware breakpoints, check out the AArch64 documentation for the DBGBCRn_EL1 register and the DBGWCRn_EL1 register

Enum/Structures

ThreadContextFlags

Bitfield of one of more of these:

Bit Bitmask Name Description
0 1 General-purpose registers If in 64-bit mode, GPRs 0–28 will be read/written. If in 32-bit mode, GPRs 0–12 will be read/written.
1 2 Control registers Reads/writes the FP, LR, PC, SP, PSTATE, and TPIDR registers.
2 4 Floating-point registers Reads/writes the floating-point vector registers.
3 8 Floating-point control registers Reads/writes the FPCR and FPSR registers.


DeviceName

Value Name
0 DeviceName_AFI
1 DeviceName_AVPC
2 DeviceName_DC
3 DeviceName_DCB
4 DeviceName_HC
5 DeviceName_HDA
6 DeviceName_ISP2
7 DeviceName_MSENCNVENC
8 DeviceName_NV
9 DeviceName_NV2
10 DeviceName_PPCS
11 DeviceName_SATA
12 DeviceName_VI
13 DeviceName_VIC
14 DeviceName_XUSB_HOST
15 DeviceName_XUSB_DEV
16 DeviceName_TSEC
17 DeviceName_PPCS1
18 DeviceName_DC1
19 DeviceName_SDMMC1A
20 DeviceName_SDMMC2A
21 DeviceName_SDMMC3A
22 DeviceName_SDMMC4A
23 DeviceName_ISP2B
24 DeviceName_GPU
25 DeviceName_GPUB
26 DeviceName_PPCS2
27 DeviceName_NVDEC
28 DeviceName_APE
29 DeviceName_SE
30 DeviceName_NVJPG
31 DeviceName_HC1
32 DeviceName_SE1
33 DeviceName_AXIAP
34 DeviceName_ETR
35 DeviceName_TSECB
36 DeviceName_TSEC1
37 DeviceName_TSECB1
38 DeviceName_NVDEC1

CodeMemoryOperation

Value Name
0 CodeMemoryOperation_MapOwner
1 CodeMemoryOperation_MapSlave
2 CodeMemoryOperation_UnmapOwner
3 CodeMemoryOperation_UnmapSlave


LimitableResource

Value Name Note
0 LimitableResource_Memory Bytes of memory a process may allocate.
1 LimitableResource_Threads Amount of threads a process can create.
2 LimitableResource_Events Amount of events a process can create through svcCreateEvent or svcSendAsyncRequestWithUserBuffer.
3 LimitableResource_TransferMemories Amount of TransferMemory a process can create through svcCreateTransferMemory.
4 LimitableResource_Sessions Amount of session a process can create through svcCreateSession, svcConnectToPort or svcConnectToNamedPort.

ProcessInfoType

Value Name
0 ProcessInfoType_ProcessState

ProcessState

Value Name Notes
0 ProcessState_Created
1 ProcessState_CreatedAttached
2 ProcessState_Started
3 ProcessState_Crashed Processes will not enter this state unless they were created with EnableDebug.
4 ProcessState_StartedAttached
5 ProcessState_Exiting
6 ProcessState_Exited
7 ProcessState_DebugSuspended

DebugThreadParam

Value Name
0 DebugThreadParam_DynamicPriority
1 DebugThreadParam_SchedulingStatus
2 DebugThreadParam_PreferredCpuCore
3 DebugThreadParam_CurrentCpuCore
4 DebugThreadParam_AffinityMask

Dynamic priority: output in out2

Scheduling status: out1 contains bit0: is debug-suspended, bit1: is user-suspended (svcSetThreadActivity 1 or svcSetProcessActivity 1). Out2 contains {suspended, idle, running, terminating} => {5, 0, 1, 4}

DebugThreadParam_PreferredCpuCore: output in out2

DebugThreadParam_CurrentCpuCore: output in out2

DebugThreadParam_AffinityMask: output in out1

CreateProcessInfo

Offset Length Bits Description
0 12 ProcessName (doesn't have to be null-terminated)
0x0C 4 ProcessCategory (0: regular title, 1: kernel built-in)
0x10 8 TitleId
0x18 8 CodeAddr
0x20 4 CodeNumPages
0x24 4 Flags
Bit0 IsAarch64
Bit3-1 #AddressSpaceType
Bit4 [2.0.0+] EnableDebug
Bit5 EnableAslr
Bit6 IsApplication
Bit7 [4.0.0] UseSecureMemory
Bit10-7 [5.0.0+] PoolPartition (0=Application, 1=Applet, 2=Sysmodule, 3=Nvservices)
Bit11 [7.0.0+] OptimizeMemoryAllocation (Only allowed in combination with IsApplication).
0x28 4 ResourceLimitHandle or zero
0x2C 4 [3.0.0+] SystemResourceNumPages

On [1.0.0] there's only one pool.

On [2.0.0-4.0.0] PoolPartition is 1 for built-ins and 0 for rest.

On [5.0.0] PoolPartition is specified in CreateProcessArgs. There are now 4 pool partitions.

On [5.0.0] (maybe lower?) a zero ResourceLimitHandle defaults to sysmodule limits and 0x12300000 bytes of memory.

The PersonalMmHeap are allocated as follows:

  • For the application, normal insecure pool is used. Carveout 5 is used to provide protection.
  • For the applet, a pre-allocated secure pool segment of size 0x400000 is used.
  • For sysmodules, secure pool is allocated.

AddressSpaceType

Type Name Width Description
0 Normal_32Bit 32
1 Normal_36Bit 36
2 WithoutMap_32Bit 32 Appears to be missing map region [?]
3 [2.0.0+] Normal_39Bit 39

MemoryInfo

Offset Length Description
0 8 BaseAddress
8 8 Size
0x10 4 MemoryType: lower 8 bits of #MemoryState
0x14 4 #MemoryAttribute
0x18 4 Permission (bit0: R, bit1: W, bit2: X)
0x1C 4 IpcRefCount
0x20 4 DeviceRefCount
0x24 4 Padding: always zero

MemoryAttribute

Bits Name Description
0 IsBorrowed Used by MapMemory, as an async IPC user buffer,
1 IsIpcLocked True when IpcRefCount > 0
2 IsDeviceShared True when DeviceRefCount > 0
3 IsUncached

MemoryState

Bits Description Meaning
7-0 Type
8 PermissionChangeAllowed
9 ForceReadWritableByDebugSyscalls Allows using #svcWriteDebugProcessMemory on segments mapped read-only.
10 IpcSendAllowed Allows sending this region as an IPC A/B/W buffer with flags=0.
11 NonDeviceIpcSendAllowed Allows sending this region as an IPC A/B/W buffer with flags=1.
12 NonSecureIpcSendAllowed Allows sending this region as an IPC A/B/W buffer with flags=3.
14 ProcessPermissionChangeAllowed
15 MapAllowed
16 UnmapProcessCodeMemoryAllowed
17 TransferMemoryAllowed
18 QueryPhysicalAddressAllowed
19 MapDeviceAllowed (#svcMapDeviceAddressSpace and #svcMapDeviceAddressSpaceByForce)
20 MapDeviceAlignedAllowed
21 IpcBufferAllowed
22 IsPoolAllocated/IsReferenceCounted The physical memory blocks backing this region are refcounted.
23 MapProcessAllowed
24 AttributeChangeAllowed
25 [4.0.0+] CodeMemoryAllowed
Value Type Meaning
0x00000000 MemoryType_Unmapped
0x00002001 MemoryType_Io Mapped by kernel capability parsing in #svcCreateProcess.
0x00042002 MemoryType_Normal Mapped by kernel capability parsing in #svcCreateProcess.
0x00DC7E03 MemoryType_CodeStatic Mapped during #svcCreateProcess.
[1.0.0+]

0x01FEBD04

[4.0.0+]

0x03FEBD04

MemoryType_CodeMutable Transition from 0xDC7E03 performed by #svcSetProcessMemoryPermission.
[1.0.0+]

0x017EBD05

[4.0.0+]

0x037EBD05

MemoryType_Heap Mapped using #svcSetHeapSize.
0x00402006 MemoryType_SharedMemory Mapped using #svcMapSharedMemory.
0x00482907 [1.0.0] MemoryType_Alias Mapped using #svcMapMemory.
0x00DD7E08 MemoryType_ModuleCodeStatic Mapped using #svcMapProcessCodeMemory.
[1.0.0+]

0x01FFBD09

[4.0.0+]

0x03FFBD09

MemoryType_ModuleCodeMutable Transition from 0xDD7E08 performed by #svcSetProcessMemoryPermission.
0x005C3C0A MemoryType_Ipc IPC buffers with descriptor flags=0.
0x005C3C0B MemoryType_Stack Mapped using #svcMapMemory.
0x0040200C MemoryType_ThreadLocal Mapped during #svcCreateThread.
0x015C3C0D MemoryType_TransferMemoryIsolated Mapped using #svcMapTransferMemory when the owning process has perm=0.
0x005C380E MemoryType_TransferMemory Mapped using #svcMapTransferMemory when the owning process has perm!=0.
0x0040380F MemoryType_ProcessMemory Mapped using #svcMapProcessMemory.
0x00000010 MemoryType_Reserved
0x005C3811 MemoryType_NonSecureIpc IPC buffers with descriptor flags=1.
0x004C2812 MemoryType_NonDeviceIpc IPC buffers with descriptor flags=3.
0x00002013 MemoryType_KernelStack Mapped in kernel during #svcCreateThread.
0x00402214 [4.0.0+] MemoryType_CodeReadOnly Mapped in kernel during #svcControlCodeMemory.
0x00402015 [4.0.0+] MemoryType_CodeWritable Mapped in kernel during #svcControlCodeMemory.

ArbitrationType

Value Type
0x0 WaitIfLessThan
0x1 DecrementAndWaitIfLessThan
0x2 WaitIfEqual

SignalType

Value Type
0x0 Signal
0x1 SignalAndIncrementIfEqual
0x2 SignalAndModifyBasedOnWaitingThreadCountIfEqual

ContinueDebugFlagsOld

[1.0.0-2.3.0]

Bit Bitmask Description
0 1 IgnoreException (note: ResumeAllThreads or debug-suspended-thread-id needed)
1 2 SwallowException
2 4 ResumeAllThreads

ContinueDebugFlags

[3.0.0+]

Bit Bitmask Description
0 1 IgnoreException (note: doesn't need to be set in the same call than Resume)
1 2 DontCatchExceptions
2 4 Resume
3 8 IgnoreOtherThreadsExceptions

IgnoreExceptionsOfOthers is like IgnoreException but acts on all threads that aren't in the input list. The affected threads are resumed.

Only one of of Resume and IgnoreOtherThreadsExceptions can be set at a time.

If the input number of threads is 0, this means "all threads".

DebugEventInfo

The below table is for the Aarch64 version of the system call. For A32, all u64 fields but title/process/thread id are actually u32, making the structure 0x28-byte-big (0x40 for a64).

Size: 0x40

Offset Length Description
0 u32 EventType
4 u32 Flags (bit0: NeedsContinue)
8 u64 ThreadId
0x10 PerTypeSpecifics

AttachProcess specific:

Offset Length Description
0x10 u64 TitleId
0x18 u64 ProcessId
0x20 char[12] ProcessName
0x2C u32 MmuFlags
0x30 u64 [5.0.0+] UserExceptionContextAddr

AttachThread specific:

Offset Length Description
0x10 u64 ThreadId
0x18 u64 TlsPtr
0x20 u64 Entrypoint

Exit specific:

Offset Length Description
0x10 u32 Type (0=PausedThread, 1=RunningThread, 2=ExitedProcess, 3=TerminatedProcess)

Exception specific:

Offset Length Description
0x10 u32 ExceptionType
0x18 u64 FaultRegister
0x20 PerExceptionSpecifics

DebugEventType

Value Name
0 DebugEvent_AttachProcess
1 DebugEvent_AttachThread
2 DebugEvent_ExitProcess
3 DebugEvent_ExitThread
4 DebugEvent_Exception

DebugExceptionType

Value Name
0 Exception_Trap (*)
1 Exception_InstructionAbort
2 Exception_DataAbortMisc (**)
3 Exception_PcSpAlignmentFault
4 Exception_DebuggerAttached
5 Exception_BreakPoint
6 Exception_UserBreak
7 Exception_DebuggerBreak
8 Exception_BadSvcId
9 Exception_SError [not in 1.0.0]

* Undefined instructions, software breakpoints, some other traps.

** Data aborts, FP traps, and everything else that doesn't belong to any of the above.

Trap specifics:

Offset Length Description
0x20 u32 Opcode

BreakPoint specifics:

Offset Length Description
0x20 u32 IsWatchpoint

UserBreak specifics:

Offset Length Description
0x20 u32 Info0
0x28 u64 Info1
0x30 u64 Info2

BadSvcId specifics:

Offset Length Description
0x20 u32 SvcId

Exception handling

First of all, a function that might be called by synchronous exception handler and that is called by the SError handler fetches the exception info, adjusts PC, panics on exceptions taken from EL1, then dispatches the exception.

The dispatcher has two mutually exclusive exception reporting methods:

  • by storing information at the start of the process's TLS memregion (TPIDRRO_EL0) and jumping back to the crt0
  • by using KDebug

KDebug dispatching is used when at least one of the following conditions are met:

  • SMC ConfigItem KernelMemConfig bit 1 is NOT set (it isn't on retail), unless: this is a software or hardware breakpoint, or a watchpoint, or [4.0.0+?] the process is attached and this is a Google PNaCl trap instruction (see LLVM source)
  • FAR doesn't point to a valid address in mapped-readable CodeStatic memory (i.e. this is the case for NRO and JIT memory) or this is one of the following exceptions (it particular, that doesn't include FP exceptions occurring in CodeStatic memory):
    • Uncategorized
    • IllegalState
    • SupervisorCallA32
    • SupervisorCallA64
    • PCAlignment
    • SPAlignment
    • SError
    • BreakpointLowerEl
    • SoftwareStepLowerEl (note: no way set single-step flag; not parsed)
    • WatchpointLowerEl
    • SoftwareBreakpointA32 (note: not parsed)
    • SoftwareBreakpointA64 (note: not parsed)

In all other cases the userland-handled exception path is taken.

KDebug path:

If the process is attached, the exception is reported to the KDebug. If the thread was continued using flag IgnoreExceptions, it returns from the exception as if nothing happened.

If the latter is not the case, or if the process isn't attached, proceed to [2.0.0+] crash reporting (or in [1.0.0] just terminate the process): if EnableDebug is set, and depending on the process state (more than one crash per process isn't permitted) it may signal itself with ProcessState_Crashed so that PM asks NS to start creport so that creport attaches to it and reports the crashes. Otherwise, just terminate.

Userland reporting path and svcReturnFromException:

TLS region start (A64):

Offset Length Description
0x0 0x148 Exception stack
0x148 0x78 ExceptionFrameA64

ExceptionFrameA64:

Offset Length Description
0x0 0x48 (8*9) GPRs 0..8.
0x48 0x8 lr
0x50 0x8 sp
0x58 0x8 pc (elr_el1)
0x60 0x4 pstate & 0xFF0FFE20
0x64 0x4 afsr0
0x68 0x4 afsr1
0x6C 0x4 esr
0x70 0x8 far

TLS region start (A32):

Offset Length Description
0x0 0x178 Exception stack
0x148 0x44 ExceptionFrameA32

ExceptionFrameA32:

Offset Length Description
0x0 0x20 (8*4) GPRs 0..7.
0x20 0x4 sp
0x24 0x4 lr
0x28 0x4 pc (elr_el1)
0x2C 0x4 tpidr_el0 = 1
0x30 0x4 cpsr & 0xFF0FFE20
0x34 0x4 afsr0
0x38 0x4 afsr1
0x3C 0x4 esr
0x40 0x4 far

In that case, after storing the regs in the TLS, the exception handler returns to the application's crt0 (entrypoint), with X0=<error description code> (see below) and X1=SP=frame=<stack top> (see above)


Desc. code Meaning
0x100 Instruction abort
0x102 Misaligned PC
0x103 Misaligned SP
0x106 SError [not in 1.0.0?]
0x301 Bad SVC
0x104 Uncategorized, CP15RTTrap, CP15RRTTrap, CP14RTTrap, CP14RRTTrap, IllegalState, SystemRegisterTrap
0x101 None of the above, EC <= 0x34 and not a breakpoint

(During normal app boot the process is invoked with X0=0 and X1=main_thread_handle. The crt0 of retail apps determines whether to boot normally or handle an exception if X0 is set to 0 or not)

The application is supposed to promptly update the contents of elr_el1 to a user handler (and any other regs it sees fit) and call svcReturnFromException (error code) to call that handler. The latter is then expected to promptly abort the program.

svcReturnFromException updates the contents of the kernel stack frame with what the user provided in the TLS structure, sets TPIDR_EL0 to 1, then:

  • if the provided error code is 0, gracefully pivots and returns from exception
  • if it is not, replays the exception and pass it to the KDebug (see above). One can pass 0x10001 to prevent process termination. If the process is attached, this also prevents crash-collection/termination (different from the exception handler behavior)

If an exception occurs from the above user handler, the entire exception handling process will repeat with the new exception.

Note that if a thread that wasn't faulting calls svcReturnFromException, it signals an "invalid syscall" exception

Note that IsDebugMode is not used during exception-handling, except for enabling printing a message to UART-A. This UART code causes a system-hang on retail (likely due to a loop that doesn't exit). This printing doesn't seem to run when the process is attached for debugging?