IOSU syscalls
This article is a stub. You can help WiiUBrew by expanding it. |
There are 2 types of syscalls:
1. Syscalls using undefined ARM instruction.
2. Syscalls using ARM syscall instruction.
Syscalls (via undefined instructions)
Similarly to the Wii's IOS, the IOSU uses a syscall table that is stored toward the end of the kernel area inside the main ARM binary (fw.img).
The second vector is the invalid instruction handler, which is used to implement syscalls:
fw:FFFF0000 LDR PC, =_reset fw:FFFF0004 LDR PC, =starbuck_syscall_handler
The Starbuck syscall handler:
starbuck_syscall_handler STMFA SP, {R0-LR}^ MRS R8, SPSR STR R8, [SP] STR LR, [SP,#0x40] LDR R10, [LR,#-4] ; R10 = E7F0XXXX (the invalid instruction) BIC R9, R10, #0xFF00 LDR R8, =0xE7F000F0 ; Syscall base CMP R9, R8 ; Were any bits set other than the syscall number BNE invalid_syscall MOV R10, R10,ASR#8 AND R10, R10, #0xFF CMP R10, #0x94 ; Max index of syscall (can possibly vary) BGT return_to_caller MOV R8, SP MOV R11, #0x1F MSR CPSR_c, R11 ; Switch to system mode and disable FIQ/IRQ LDR R8, [R8,#0x44] LDR R11, =syscall_stack_arg_counts LDR R11, [R11,R10,LSL#2] ; Number of args on stack for this syscall ADD SP, SP, R11,LSL#2 get_stack_arg CMP R11, #0 BEQ find_syscall_and_jump LDR R9, [SP,#-4]! ; Copy argument value STR R9, [R8,#-4]! SUB R11, R11, #1 B get_stack_arg find_syscall_and_jump MOV SP, R8 LDR R11, =syscall_table LDR R11, [R11,R10,LSL#2] MOV LR, PC BX R11 return_to_caller MOV R11, #0xDB ; Switch to undefined mode and re-enable FIQ/IRQ MSR CPSR_c, R11 LDR R11, [SP] MSR SPSR_cxsf, R11 MOV LR, R0 LDMED SP, {R0-LR}^ NOP MOV R0, LR LDR LR, [SP,#0x40] MOVS PC, LR ; Return invalid_syscall LDR SP, =current_thread_ctx_addr LDR SP, [SP] STR LR, [SP,#0x40] ADD SP, SP, #0x40 STMFD SP, {R0-LR}^ SUB R0, SP, #0x40 MOV LR, #6 ; STATE_FAULTED STR LR, [R0,#0x50] LDR SP, =debug_args_addr BL debug_print ; Illegal Instruction:tid=%d,pid=%d,pc=0x%08x,sp=0x%08x B schedule_yield
Syscalls are invoked by way of the invalid instruction handler; syscalls take the form 0xE7F000F0 | (syscall_num << 8). (E.g. E7F000F0 is syscall 0, E7F036F0 is syscall 0x36, etc.).
The IOSU currently has 0x94 available syscalls (the number of installed syscalls can vary between system versions).
NOTE: Official syscall names begin with "IOS_", the rest are merely educated guesses.
ID # | Internal name | Description | Return value |
---|---|---|---|
0x00 | int IOS_CreateThread(u32 (*proc)(void* arg), void* arg, u32* stack_top, u32 stacksize, int priority, BOOL detached) | Creates a thread (in paused state) | New threadid or error (negative value) |
0x01 | int thread_join(int threadid, u32 *returned_value) | Waits for a thread to finish executing | 0 on success |
0x02 | int thread_cancel(int threadid, u32 unk) | Cancels an active thread | 0 on success |
0x03 | int get_tid() | Get the current thread's ID | Current threadid |
0x04 | |||
0x05 | int get_pid() | Get the current process' ID | Current processid |
0x06 | int get_process_name(int pid, char *out_buffer, u32 out_size) | Get the specified process' name string | 0 on success |
0x07 | int thread_resume(int threadid) | Resume the specified thread | 0 on success |
0x08 | int thread_suspend(int threadid) | Suspend the specified thread | 0 on success |
0x09 | int thread_yield() | Yield execution to any higher priority threads | 0 on success |
0x0A | int IOS_GetThreadPriority(int threadid) | Get the priority of the specified thread | The thread's priority or error (negative value) |
0x0B | int IOS_SetThreadPriority(int threadid, int priority) | Set the priority of the specified thread | 0 on success |
0x0C | int IOS_CreateMessageQueue(u32 *ptr, u32 n_msgs) | Create a queue at ptr, for n_msgs messages | The queue ID |
0x0D | int IOS_DestroyMessageQueue(int queueid) | Destroy a message queue | 0 on success |
0x0E | int IOS_SendMessage(int queueid, u32 message, u32 flags) | Add a message to the end queue | 0 on success |
0x0F | int IOS_JamMessage(int queueid, u32 message, u32 flags) | Add a message to the front of a queue | 0 on success |
0x10 | int IOS_ReceiveMessage(int queueid, u32 *message, u32 flags) | Fetch a message from the front of a queue | 0 on success |
0x11 | int IOS_HandleEvent(int device, int queueid, int message) | Register queueid as a handler for interrupts generated by device (sends message to queueid when device's interrupt is triggered) | 0 on success |
0x12 | int unregister_event_handler(int device) | Unregister handler for device | 0 on success |
0x13 | int IOS_CreateTimer(int time_us, int repeat_time_us, int queueid, u32 message) | Create a timer that sends a message to a queue after the elapsed period(s) | Timerid or error (negative value) |
0x14 | int IOS_RestartTimer(int timerid, int time_us, int repeat_time_us) | Restart a timer using the specified period(s) | 0 on success |
0x15 | int IOS_StopTimer(int timerid) | Pauses the specified timer | 0 on success |
0x16 | int IOS_DestroyTimer(int timerid) | Destroys the specified timer | 0 on success |
0x17 | |||
0x18 | int time_now() | ||
0x19 | IOS_GetUpTimeStruct(???) | ||
0x1A | IOS_GetUpTime64(???) | ||
0x1B | |||
0x1C | IOS_GetAbsTimeCalendar(???) | ||
0x1D | IOS_GetAbsTime64(???) | ||
0x1E | IOS_GetAbsTimeStruct(???) | ||
0x1F | |||
0x20 | |||
0x21 | int check_jtag() | Get the current status of the JTAG | 0 if JTAG is enabled or -4 if disabled |
0x22 | |||
0x23 | int heap_create(void *ptr, int size) | Create a new heap at ptr of size bytes | The heapid or error (negative value) |
0x24 | int IOS_CreateLocalProcessHeap(void *ptr, int size) | Create a new local process heap of size bytes | The heap ID or error (negative value) |
0x25 | int IOS_CreateCrossProcessHeap(int size) | Create a new cross process heap of size bytes | The heap ID or error (negative value) |
0x26 | int heap_destroy(int heapid) | Destroy the specified heap | 0 on success |
0x27 | void* IOS_Alloc(int heapid, u32 size) | Allocate size bytes from the specified heap | Pointer to memory |
0x28 | void* heap_alloc_aligned(int heapid, u32 size, u32 align) | Allocate size bytes from the specified heap with the requested alignment | Pointer to aligned memory |
0x29 | void IOS_Free(int heapid, void *ptr) | Release allocated memory back to the heap | 0 on success |
0x2A | |||
0x2B | |||
0x2C | |||
0x2D | BOOL device_register(const char* device, int queueid) | Registers device to the device tree, so it can be opened (from Starbuck and PPC) | 0 on success |
0x2E | |||
0x2F | |||
0x30 | |||
0x31 | |||
0x32 | int query_featureid(int featureid, int out_size, void *out_buffer) | ||
0x33 | int IOS_Open(const char* device, int mode) | Similar to IOS_Open on PPC, except now internal to the IOSU system | Returns an fd or error (negative) |
0x34 | int IOS_Close(int fd) | Close a previously opened fd | 0 on success |
0x35 | int IOS_Read(int fd, void *buf, u32 len) | Read len bytes from fd into buf | The number of bytes read or error |
0x36 | int IOS_Write(int fd, const void *buf, u32 len) | Write len bytes to fd from buf | The number of bytes written or error |
0x37 | int IOS_Seek(int fd, int offset, int origin) | Seek to offset relative to origin | The new absolute offset or error |
0x38 | int IOS_Ioctl(int fd, u32 request, void *input_buffer, u32 input_buffer_len, void *output_buffer, u32 output_buffer_len) | Perform the requested IOCTL | Return value from IOCTL |
0x39 | int IOS_Ioctlv(int fd, u32 request, u32 vector_count_in, u32 vector_count_out, struct iovec *vector) | Perform the requested IOCTL | Return value from IOCTL |
0x3A | int IOS_OpenAsync(const char* device, int mode, int queueid, ipcmessage *message) | Async implementation of IOS_Open | 0 on success, ipcmessage is sent to the queue with the command's return value |
0x3B | int IOS_CloseAsync(int fd, int queueid, ipcmessage *message) | Async implementation of IOS_Close | 0 on success |
0x3C | int IOS_ReadAsync(int fd, void *buf, u32 len, int queueid, ipcmessage *message) | Async implementation of IOS_Read | 0 on success |
0x3D | int IOS_WriteAsync(int fd, const void *buf, u32 len, int queueid, ipcmessage *message) | Async implementation of IOS_Write | 0 on success |
0x3E | int IOS_SeekAsync(int fd, int offset int origin, int queueid, ipcmessage *message) | Async implementation of IOS_Seek | 0 on success |
0x3F | int IOS_IoctlAsync(int fd, u32 request, void *input_buffer, u32 input_buffer_len, void *output_buffer, u32 output_buffer_len, int queueid, ipcmessage *message) | Async implementation of IOS_Ioctl | 0 on success |
0x40 | int IOS_IoctlvAsync(int fd, u32 request, u32 vector_count_in, u32 vector_count_out, struct iovec *vector, int queueid, ipcmessage *message) | Async implementation of IOS_Ioctlv | 0 on success |
0x41 | open_as_async | ||
0x42 | write_as_async | ||
0x43 | ipc_resume | ||
0x44 | ipc_suspend | ||
0x45 | ipc_svcmsg | ||
0x46 | ipc_resume_async | ||
0x47 | ipc_suspend_async | ||
0x48 | ipc_svcmsg_async | ||
0x49 | IOS_ResourceReply(???) | ||
0x4A | |||
0x4B | |||
0x4C | |||
0x4D | |||
0x4E | |||
0x4F | |||
0x50 | IOS_ClearandEnable(???) | ||
0x51 | access_iobuf_pool(???) | ||
0x52 | |||
0x53 | |||
0x54 | |||
0x55 | |||
0x56 | |||
0x57 | |||
0x58 | |||
0x59 | |||
0x5A | |||
0x5B | int IOS_InvalidateDCache(void *ptr, unsigned int len) | ||
0x5C | |||
0x5D | disable_memory_protection | ||
0x5E | |||
0x5F | |||
0x60 | |||
0x61 | IOS_CreateSemaphore(???) | ||
0x62 | IOS_WaitSemaphore(???) | ||
0x63 | IOS_SignalSemaphore(???) | ||
0x64 | IOS_DestroySemaphore(???) | ||
0x65 | |||
0x66 | |||
0x67 | |||
0x68 | |||
0x69 | |||
0x6A | get_iop_cpu_utilization(???) | ||
0x6B | |||
0x6C | IOS_ThreadProfileCommand(???) | ||
0x6D | get_thread_utilization(???) | ||
0x6E | |||
0x6F | |||
0x70 | |||
0x71 | get_iobuf_utilization(???) | ||
0x72 | get_message_utilization(???) | ||
0x73 | get_active_resources(???) | ||
0x74 | |||
0x75 | get_timer_utilization(???) | ||
0x76 | get_semaphore_utilization(???) | ||
0x77 | |||
0x78 | |||
0x79 | |||
0x7A | |||
0x7B | |||
0x7C | |||
0x7D | |||
0x7E | |||
0x7F | panic(???) | ||
0x80 | crash(???) | ||
0x81 | |||
0x82 | |||
0x83 | |||
0x84 | |||
0x85 | |||
0x86 | |||
0x87 | |||
0x88 | |||
0x89 | |||
0x8A | |||
0x8B | |||
0x8C | |||
0x8D | |||
0x8E | get_resource_violations(???) | ||
0x8F | |||
0x90 | |||
0x91 | |||
0x92 | |||
0x93 |
Syscalls (via ARM syscall instruction)
These types of syscalls are created with the ARM syscall instruction. The exception handler is empty and just do nothing and returns. These syscalls are RealView semihosting operations that allow communication with a debugger.
Currently only syscall 0x04 is still used in production versions of IOSU.
MOVS r0, #syscall_number
SVC 0xAB
Register r0 takes the syscall number.
Register r1 takes the first parameter.
ID # | Internal name | Description | Return value |
---|---|---|---|
0x04 | write(const char *string) | Prints a null-terminated debug message | None |
0x05 | unused | ||
0x06 | unused | ||
0x07 | unused | ||
0x08 | unused | ||
0x09 | unused | ||
0x0A | unused | ||
0x0C | unused | ||
0x0D | unused | ||
0x0E | unused | ||
0x0F | unused | ||
0x10 | unused | ||
0x11 | unused | ||
0x12 | unused | ||
0x13 | unused | ||
0x15 | unused | ||
0x16 | unused | ||
0x30 | unused | ||
0x31 | unused |