System calls

Id Name In Out
0x1 #svcSetHeapSize X1=size W0=result, X1=outaddr
0x2 #svcProtectMemory X0=addr, X1=size, W2=prot W0=result
0x3 #svcSetMemoryState X0=addr, X1=size, W2=state0, W3=state1 W0=result
0x4 #svcMirrorStack X0=dstaddr, X1=srcaddr, X2=size W0=result
0x5 svcUnmirrorStack X0=dstaddr, X1=srcaddr, X2=size W0=result
0x6 svcQueryMemory X0=meminfo_ptr, X2=addr W0=result, W1=pageinfo
0x7 svcExitProcess None
0x8 #svcCreateThread X1=entry, X2=arg, X3=stacktop, W4=prio, W5=processor_id W0=result, W1=handle
0x9 svcStartThread W0=thread_handle
0xA svcExitThread None
0xB #svcSleepThread X0=nano
0xC svcGetThreadPriority W1=thread_handle W0=result, W1=prio
0xD svcSetThreadPriority W0=thread_handle, W1=prio W0=result
0xE svcGetThreadAffinityMask W2=thread_handle W0=result, W1=out, X2=out
0xF svcSetThreadAffinityMask W0=thread_handle, W1=in, X2=in2 W0=result
0x10 svcGetCurrentProcessorNumber None W0/X0=cpuid
0x11 svcGetMemoryBlockSomethingA? W0=handle ?
0x12 svcGetMemoryBlockSomethingB? W0=handle ?
0x13 svcMapMemoryBlock W0=memblk_handle, X1=addr, X2=size, W3=perm W0=result
0x14 svcUnmapMemoryBlock W0=memblk_handle, X1=addr, X2=size W0=result
0x15 #svcCreateMemoryMirror X1=addr, X2=size, W3=perm W0=result, W1=handle
0x16 svcCloseHandle W0=handle W0=result
0x17 svcClearEvent W0=handle W0=result
0x18 #svcWaitEvents X1=handles_ptr, X2=num_handles. X3=timeout W0=result, W1=handle_idx
0x19 svcSignalEvent W0=handle W0=result
0x1A svcLockMutex W0=cur_thread_handle, X1=ptr, W2=req_thread_handle
0x1B svcUnlockMutex X0=ptr
0x1C svcCondWait X0=ptr0, X1=ptr, W2=thread_handle, X3=timeout W0=result
0x1D svcCondBroadcast X0=ptr, W1=value W0=result
.... ? ? ?
0x1F svcConnectToPort X1=port_name_str W0=result, W1=handle
.... ? ? ?
0x21 svcSendSyncRequest X0=handle W0=result
0x22 svcSendSyncRequestByBuf X0=cmdbufptr, X1=size, X2=handle W0=result
.... ? ? ?
0x25 svcGetThreadId W0=thread_handle W0=result, X1=out
0x26 svcBreak X0,X1,X2=info ?
0x27 svcOutputDebugString X0=str, X1=size
0x28 svcReturnFromException X0=result
0x29 #svcGetInfo X1=info_id, X2=handle, X3=info_sub_id W0=result, X1=out
.... ? ? ?
0x40 ??? W2=?, X3=? W0=result, W1=?, W2=?
0x41 svcAcceptSession W1=port_handle W0=result, W1=session_handle
.... ? ? ?
0x43 svcReplyAndReceive X1=ptr_handles, W2=num_handles, X3=?, X4=timeout W0=result, W1=handle_idx
0x44 svcReplyAndReceiveByBuf X1=buf, X2=sz, X3=ptr_handles, W4=num_handles, X5=?, X6=timeout W0=result, W1=handle_idx
0x45 svcCreateEvent? None W0=result, W1=?, W2=?
.... ? ? ?
0x50 svcCreateMemoryBlock W1=size?, W2=perm0, W3=perm1 W0=result, W1=handle
0x51 #svcMapMemoryMirror X0=mirror_handle, X1=addr, X2=size, W3=perm W0=result
0x52 #svcUnmapMemoryMirror W0=mirror_handle, X1=addr, X2=size W0=result

svcSetHeapSize

Size must be a multiple of 0x2000000.

svcProtectMemory

Bit2 of permission (exec) is not allowed.

Setting write-only is not allowed either (bit1).

svcSetMemoryState

State0 State1 Action
0 0 Clear bit35 in #MemoryState.
8 0 Clear bit35 in #MemoryState.
8 8 Set bit35 in #MemoryState.

This might used for switching between cached and non-cached mappings.

svcMirrorStack

Memory is only allowed to be mapped into a special region.

Code can get the range of this region from #svcGetInfo.

The source region gets reprotected to ---, and sets bit32 is set in #MemoryState.

svcCreateThread

Processor_id must be 0,1,2,3 or -2.

svcSleepThread

Setting nano=0 means "yield thread".

svcCreateMemoryMirror

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

Executable bit perm not allowed.

Closing the returned handle automatically causes the bit32 in #MemoryState to clear.

svcWaitSynchronizationN

Works with num_handles <= 0x40, error on num_handles == 0.

Does not accept 0xFFFF8001 or 0xFFFF8000 as handles.

svcBreak

On retail when called with all 3 input params set to value 0, the process will terminate. The system handles the process terminating this way the same as when the process crashes.

svcGetInfo

Handle type Id0 Id1 Description
Process 0 0 Code available mask. Always 0xF meaning all 4 cores available.
Process 1 0 Always 0xfffffffff0000000.
Process 2 0 Randomized unknown base-address.
Process 3 0 Always 0x1000000000.
Process 4 0 Randomized heap base-address.
Process 5 0 Always 0x180000000.
Process 6 0 Total memory usage?
Process 7 0 Process heap size.
Zero 8 0 Always 0. Used during exception handling.
Zero 9 0 This creates and returns an unknown handle.
Zero 10 -1, 0 ?
Zero 11 0-3 Returns random from TRNG. Used to seed usermode PRNGs.
Process 12 0 Always 0x8000000.
Process 13 0 Always 0x7ff8000000.
Process 14 0 Start of stack mirror region.
Process 15 0 Size of stack mirror region.

svcMapMemoryMirror

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

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

svcUnmapMemoryMirror

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

MemoryState

Lower 8 bits Type Meaning
0x3 Code static .text and .rodata
0x4 Code .data
0x5 Heap
0x6 Shared memory block
0x8 Module code static .text and .rodata
0x9 Module code .data
0xB Stack mirror
0xC Thread local storage
0xE Memory mirror
0x10 Reserved

Bit32: is_mirrored

Bit35: is_uncached?

Exception Handling

There appears to be userland code for handling exceptions, however this doesn't seem to be executed on retail.

On usermode exception, it jumps to main code binary entrypoint (main_binary_address+0) with X0=exception_info_ptr and X1=exception_info2_ptr. On boot, X0 is set to 0 triggering normal crt0 setup.