In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

IOS/Syscalls

From WiiBrew
< IOS
Jump to navigation Jump to search

There are 2 types of syscalls:

1. Syscalls using undefined ARM instruction.

2. Syscalls using ARM syscall instruction.

Syscalls (via undefined instructions)

Internally, IOS uses a syscall table that is stored toward the end of the main kernel binary. The exact address varies with version of IOS, but there are two methods to locate it:

ELF header:

The second-to-last program header is the syscall table. For example:

$ arm-eabi-readelf -l ~/wii/system_updates/boot2.elf  | tail -2
 LOAD           0x0230d5 0xffff7f60 0xffff7f60 0x00a88 0x00a88 RW  0x10   # syscall table
 LOAD           0x023b5d 0xffff8a00 0xffff8a00 0x00000 0x071e8 RW  0x20 # kernel BSS

or

elf_header:134C01A0 elf32_phdr < 1, 0x230D5, syscall_base, syscall_base, 0xA88, 0xA88,  6, 0x10>
elf_header:134C01C0 elf32_phdr < 1, 0x23B5D, current_thread_context, current_thread_context, 0, 0x71E8,  6, 0x20>

Syscall Handler: The second vector is the invalid instruction handler, which is used to implement syscalls:

kernel:FFFF0000               LDR     PC, =_reset
kernel:FFFF0004               LDR     PC, =starlet_syscall_handler

kernel:FFFF1F24             starlet_syscall_handler                 ; CODE XREF: start�j
kernel:FFFF1F24 E9 CD 7F FF                 STMFA   SP, {R0-LR}^
kernel:FFFF1F28 E1 4F 80 00                 MRS     R8, SPSR
kernel:FFFF1F2C E5 8D 80 00                 STR     R8, [SP,#spsr_register_save]
kernel:FFFF1F30 E5 8D E0 40                 STR     LR, [SP,#lr_register_save]
kernel:FFFF1F34 E5 1E A0 04                 LDR     R10, [LR,#-4]   ; R10 = E600XXXX  (the invalid instruction)
kernel:FFFF1F38 E3 CA 9D 7F                 BIC     R9, R10, #NOT 0xFFFFE03F
kernel:FFFF1F3C E5 9F 84 CC                 LDR     R8, =0xE6000010 ; syscall base
kernel:FFFF1F40 E3 C9 90 20                 BIC     R9, R9, #NOT 0xFFFFFFDF ; R9 = R10 & FFFFE01F
kernel:FFFF1F44 E1 59 00 08                 CMP     R9, R8          ; Were any bits set other than the syscall number
kernel:FFFF1F48 1A 00 00 1F                 BNE     invalid_syscall
kernel:FFFF1F4C E1 A0 A2 CA                 MOV     R10, R10,ASR#5  ; R10 = R10 >> 5
kernel:FFFF1F50 E2 0A A0 FF                 AND     R10, R10, #0xFF ; R10 = R10 & 0xFF
kernel:FFFF1F54 E3 5A 00 7A                 CMP     R10, #0x7A      ; max index of syscall (can vary for each IOS)
kernel:FFFF1F58 CA 00 00 11                 BGT     return_to_caller
kernel:FFFF1F5C E1 A0 80 0D                 MOV     R8, SP
kernel:FFFF1F60 E3 A0 B0 1F                 MOV     R11, #0b11111
kernel:FFFF1F64 E1 21 F0 0B                 MSR     CPSR_c, R11     ; switch to system mode, disable irq & fiq
kernel:FFFF1F68 E5 98 80 44                 LDR     R8, [R8,#sp_register_save]
kernel:FFFF1F6C E5 9F B4 A0                 LDR     R11, =syscall_stack_arg_counts
kernel:FFFF1F70 E7 9B B1 0A                 LDR     R11, [R11,R10,LSL#2] ; number of args on stack for this syscall
kernel:FFFF1F74 E0 8D D1 0B                 ADD     SP, SP, R11,LSL#2 ; SP += R11[R10 << 2]
kernel:FFFF1F78             get_stack_arg                            ; CODE XREF: start+1F8C�j
kernel:FFFF1F78 E3 5B 00 00                 CMP     R11, #0
kernel:FFFF1F7C 0A 00 00 03                 BEQ     find_syscall_and_jump
kernel:FFFF1F80 E5 3D 90 04                 LDR     R9, [SP,#-4]! ; copy argument value
kernel:FFFF1F84 E5 28 90 04                 STR     R9, [R8,#-4]!
kernel:FFFF1F88 E2 4B B0 01                 SUB     R11, R11, #1
kernel:FFFF1F8C EA FF FF F9                 B       get_stack_arg
kernel:FFFF1F90             find_syscall_and_jump                   ; CODE XREF: start+1F7C�j
kernel:FFFF1F90 E1 A0 D0 08                 MOV     SP, R8
kernel:FFFF1F94 E5 9F B4 7C                 LDR     R11, =syscall_table
kernel:FFFF1F98 E7 9B B1 0A                 LDR     R11, [R11,R10,LSL#2] ; syscall number * sizeof(dword) + syscall table offset
kernel:FFFF1F9C E1 A0 E0 0F                 MOV     LR, PC          ; save returning position
kernel:FFFF1FA0 E1 2F FF 1B                 BX      R11             ; jump to syscall implementation
kernel:FFFF1FA0                                                     ;
kernel:FFFF1FA0                                                     ; NOTE: every syscall function
kernel:FFFF1FA0                                                     ; ends with something like "bx lr"
kernel:FFFF1FA4
kernel:FFFF1FA4             return_to_caller                        ; CODE XREF: start+1F58�j
kernel:FFFF1FA4 E3 A0 B0 DB                 MOV     R11, #0xDB      ; switch to undefined mode + re-enable FIQ/IRQ
kernel:FFFF1FA8 E1 21 F0 0B                 MSR     CPSR_c, R11
kernel:FFFF1FAC E5 9D B0 00                 LDR     R11, [SP,#spsr_register_save]
kernel:FFFF1FB0 E1 6F F0 0B                 MSR     SPSR_cxsf, R11  ; restore spsr
kernel:FFFF1FB4 E1 A0 E0 00                 MOV     LR, R0
kernel:FFFF1FB8 E9 DD 7F FF                 LDMED   SP, {R0-LR}^
kernel:FFFF1FBC E1 A0 00 00                 NOP
kernel:FFFF1FC0 E1 A0 00 0E                 MOV     R0, LR
kernel:FFFF1FC4 E5 9D E0 40                 LDR     LR, [SP,#lr_register_save]
kernel:FFFF1FC8 E1 B0 F0 0E                 MOVS    PC, LR          ; return to caller
kernel:FFFF1FCC
kernel:FFFF1FCC             invalid_syscall                            ; CODE XREF: start+1F48�j
kernel:FFFF1FCC E5 9F D4 48                 LDR     SP, =current_thread_context_addr
kernel:FFFF1FD0 E5 9D D0 00                 LDR     SP, [SP,#spsr_register_save]
kernel:FFFF1FD4 E5 8D E0 40                 STR     LR, [SP,#lr_register_save]
kernel:FFFF1FD8 E3 A0 E0 06                 MOV     LR, #6 ; STATE_FAULTED
kernel:FFFF1FDC E5 8D E0 50                 STR     LR, [SP,#thread_state] ; segfault, invalid instruction
kernel:FFFF1FE0 E2 8D D0 04                 ADD     SP, SP, #4
kernel:FFFF1FE4 E9 4D 7F FF                 STMFD   SP, {R0-LR}^
kernel:FFFF1FE8 E1 4F B0 00                 MRS     R11, SPSR
kernel:FFFF1FEC E5 0D B0 04                 STR     R11, [SP,#-4+spsr_register_save]
kernel:FFFF1FF0 EA 00 00 D2                 B       schedule_yield
kernel:FFFF1FF0             ; End of function starlet_syscall_handler

Syscalls are invoked by way of the invalid instruction handler; syscalls take the form 0xE6000010 | (syscall_num << 5). (E.g. E6000010 is syscall 0, E60006D0 is syscall 0x36, etc.). IOS70 has 0x7A available syscalls.

tmbinc has written an IDAPython script which can take a database that has "syscall_base" defined, and transform the references to it into more meaningful things -- it is available here: IOS/Syscall IDAPython

(please feel free to contribute your own findings!)

ID # Internal name Description Return value
0 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)
1 int thread_join(int threadid, u32 *returned_value) Waits for a thread to finish executing 0 on success
2 int thread_cancel(int threadid, u32 return_value ) Ends a thread, called automatically when proc returns 0 on success
3 int get_tid(void) Get the current thread's ID Current threadid
4 int get_pid(void) Get the current process's ID Current processid
5 int IOS_StartThread(int threadid) Resume the specified thread 0 on success
6 int thread_suspend(int threadid) Suspend the specified thread 0 on success
7 int thread_yield(void) Yield execution to any higher priority threads 0 on success
8 int IOS_GetThreadPriority(int threadid) Get the priority of the specified thread thread's priority or error (negative value)
9 int IOS_SetThreadPriority(int threadid, int priority) Set the priority of the specified thread 0 on success
a int IOS_CreateMessageQueue(u32 *ptr, u32 n_msgs) Create a queue at ptr, for n_msgs messages The queue ID
b int IOS_DestroyMessageQueue(int queueid) Destroy a message queue 0 on success
c int IOS_SendMessage(int queueid, u32 message, u32 flags) Add a message to the end queue 0 on success
d int message_queue_send_now(int queueid, u32 message, u32 flags) Add a message to the front of a queue 0 on success
e int IOS_ReceiveMessage(int queueid, u32 *message, u32 flags) Fetch a message from the front of a queue 0 on success
f 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
10 int UnregisterEventHandler(int device) Unregister handler for device 0 on success
11 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)
12 int IOS_RestartTimer(int timerid, int time_us, int repeat_time_us) Restart a timer using the specified period(s) 0 on success
13 int IOS_StopTimer(int timerid) Pauses the specified timer 0 on success
14 int IOS_DestroyTimer(int timerid) Destroys the specified timer 0 on success
15 u32 time_now() Fetch the current value of starlet's timer The current value of the HW_TIMER register
16 int heap_create(void *ptr, int size) Create a new heap at ptr of size bytes heapid or error (negative value)
17 int heap_destroy(int heapid) Destroy the specified heap 0 on success
18 void* IOS_Alloc(int heapid, u32 size) Allocate size bytes from the specified heap pointer to memory
19 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
1a void IOS_Free(int heapid, void *ptr) Release allocated memory back to the heap
1b BOOL IOS_RegisterResourceManager(const char* device, int queueid) Registers device to the device tree, so it can be opened (from Starlet and PPC) 0 on success
1c int IOS_Open(const char* device, int mode) Similar to IOS_Open on PPC, except now internal to the IOS system Returns an fd or error (negative)
1d int IOS_Close(int fd) Close a previously opened fd 0 on success
1e int IOS_Read(int fd, void *buf, u32 len) Read len bytes from fd into buf The number of bytes read or error
1f int IOS_Write(int fd, const void *buf, u32 len) Write len bytes to fd from buf The number of bytes written or error
20 int IOS_Seek(int fd, int offset, int origin) Seek to offset relative to origin The new absolute offset or error
21 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
22 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
23 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
24 int IOS_CloseAsync(int fd, int queueid, ipcmessage *message) Async implementation of IOS_Close 0 on success
25 int IOS_ReadAsync(int fd, void *buf, u32 len, int queueid, ipcmessage *message) Async implementation of IOS_Read
26 int IOS_WriteAsync(int fd, const void *buf, u32 len, int queueid, ipcmessage *message) Async implementation of IOS_Write
27 int IOS_SeekAsync(int fd, int offset int origin, int queueid, ipcmessage *message) Async implementation of IOS_Seek
28 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
29 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
2a void IOS_ResourceReply( const struct ios_resource_request *request, int retval) return from a cmd on a resource
2b SetUID(u32 pid, u32 gid)
2c get_hmac_queue_for_pid
2d s32 SetGID(u32 pid, u16 gid)
2e u16 GetCurrentProcessGid()
2f cc_ahbMemFlush
30 syscall_ahbMemFlush_wrapper
31 software_IRQ_31 seems to enable hardware interrupts for device nr 31
32 software_irq_18 seems to enable hardware interrupts for device nr 18
33 software_IRQ_7_or_8(id) seems to enable hardware interrupts for device nr 7 if id==0, else device nr 8 (sdhc)
34 software_IRQ(id) enables hardware interrupts for device nr. id; check caller PID
35 access_iobuf_pool(arg1) no-op in IOS-35, arg1=0 returns always 0
36 struct iobuf *alloc_iobuf(arg1, sbuf) allocate an iobuf, arg1=0 (unknown), sbuf = buffer size return NULL on error
37 int free_iobuf(struct iobuf *buf) free an allocated iobuf
38 iobuf_log_header_info
39 iobuf_log_buffer_info
3a void *extend_iobuf(struct iobuf *iob, unsigned short num) extend the data in the buffer by num bytes returns pointer to extended area
3b void *IOS_PushIob(struct iobuf *iob, unsigned short num) move head pointer in io buffer num bytes towards the buffer end returns old head pointer
3c void *IOS_PullIob(struct iobuf *iob, unsigned short num) move head pointer in io buffer num bytes towards the buffer start
3d int verify_iobuf(struct iobuf *iob) verify if the argument points to an io buffer
3e syscall_3e
3f void sync_before_read(void *address, u32 size) Invalidates dcache, and something (probably related to flushing memory)
40 void sync_after_write(const void *address, u32 size) Flushes dcache and does magic bullshit (aka magic AHB operations)
41 ppc_boot(const char *path) Loads a .dol or .elf file into memory and bootstraps the PPC
42 ios_boot(const char *path, u32 flag, u32 version) Loads a new IOS kernel
43 syscall_43
44 int syscall_assert_di_reset Clears bit 10 of 0xD800194 Returns 0 on success, -1 on error
45 int syscall_deassert_di_reset Enables bit 10 of 0xD800194 Returns 0 on success, -1 on error
46 BOOL syscall_check_di_reset Checks bit 10 of 0xD800194 Returns 1 on reset asserted, 0 on (deasserted or error)
47 GetSomeFlags Depending on what is currently running (boot2/IOS) it returns different values IOS: *(u32*)r0=0 *(u16*)r1=0

Boot2: *(u32*)r0=3 *(u16*)r1=0

48 set_r0_1_r1_0
49 get_boot_vector
4a GetHollywoodRevision
4b void kernel_debug_print(u32 flags) Prints various debug info (depending on flags) from the kernel
4c int kernel_set_version(u32 version) Stores version to 0x3140 (can only be called by ES) 0 on success
4d u32 kernel_get_version() Returns the current IOS version from 0x3140 (can only be called by ES IOS version or 0 on error
4e poke_E0_1 Can only be called by DI
4f void* virt_to_phys(void *ptr) Converts a virtual pointer to its physical equivalent
50 void Set_DVDVideo(u32 disable) Enable/Disable DI DVD Video commands (can only be called from DI) 0 on success, -1 on error
51 u32 Check_DVDVideo() Return status of DI DVD Video commands (can only be called from DI) 1 if disabled, 0 if enabled or error
52 syscall_52 can only be called from DI
53 syscall_53 can only be called from DI
54 int set_ahbprot(u32 enable) Enable/Disable PPC AHBPROT setting (can only be called from ES) 0 on success, -1 on error
55 GetBUSClock Returns either 162(GC) or 243(Wii)
56 int poke_gpios(u32 reg, u32 value) Set gpio reg to value (can only be called from STM) 0 on success, -1 on error
57 syscall_57 can only be called from STM 0 on success, -1 on error
58 void call_poke_debug_port(u8 value) Set GPIO lines 16-23 (DEBUG1-7) to value
59 load_PPC can only be called from ES 0 on success, negative for error
5a u32 load_module(const char* path) Load the specified IOS module and run it (can only be called from UID 0, which is kernel or ES) 0 on success, negative for error
ENUMS KEY_USAGE KEY_TYPE KEY_IDS
crypto aes ECC Private Key
signature hmac Console ID
ECC Pair 2048b Certificate NAND AES Key
Informative 4096b Certificate NAND HMAC
ECC Common Key
Console ID PRNG Seed
SEEPROM Counter SD Key
Boot2 Version
?
?
SEEPROM NAND Generation
Korean Common Key
5b int create_key(int *keyid, KEY_USAGE usage, KEY_TYPE type) create a new keyring entry 0 on success, negative for error
5c int IOSC_DeleteObject(int keyid) Remove a keyring entry 0 on success, negative for error
5d int initialize_key(int *keyid, u32, int decryption_keyid, u32, u32, void *iv, void *crypted_key) Sets the contents of a key 0 on success, negative for error
5e keyring_deallocate_entry(int index) 0 on success, negative for error
5f int set_sig_data(const void *data, const u32 *userdata, int keyid) Sets the contents of a signature. data length should match the signature type, user_data is optional 4 bytes that can be attached 0 on success, negative for error
60 int get_sig_info(void *data, u32 *userdata, int keyid) Gets the contents of a signature 0 on success, negative for error
61 int calc_ecdh_shared_secret(int self_ecc_keyid, int sender_ecc_public_keyid, int dest_keyid) Generates a new AES crypto key from an ecdh shared secret calculated from a sender's ECC key and our own ECC key 0 on success, negative for error
62 int set_4byte_key(int keyid, const u32 *data) 0 on success, negative for error
63 int get_4byte_key(int keyid, u32 *data) Fetch 4 bytes of userdata from the key 0 on success (userdata in data), negative for error
64 int get_key_size(int keyid, u32 *size) Return the key's size in size[0] 0 on success, negative for error
65 int get_key_userdata_size(int keyid, u32 *size) Return the key's userdata size in size[0] 0 on success, negative for error
66 int IOSC_GenerateHashAsync(void *SHACarry, const void *data, u32 length, u32 SHAMode, void *hash, int queueid, ipcmessage *message) Calculate SHA1 hash of data, send message to queueid with result 0 on success
67 int IOSC_GenerateHash(void *SHACarry, const void *data, u32 length, u32 SHAMode, void *hash) Synchronous implementation of IOSC_GenerateHashAsync 0 on success
68 int IOSC_EncryptAsync(int keyid, void *iv, const void *in, int len, void *out, int queueid, ipcmessage *message) AES-encrypt len bytes from in using keyid and iv (which gets updated) and write to out. Send message to queueid with result 0 on success
69 int IOSC_Encrypt(int keyid, void *iv, const void *in, int len, void *out) Synchronous implementation of IOSC_EncryptAsync 0 on success
6a int IOSC_DecryptAsync(int keyid, void *iv, const void *in, int len, void *out, int queueid, ipcmessage *message) AES-decrypt len bytes from in using keyid and iv (which gets updated) and write to out. Send message to queueid with result 0 on success
6b int IOSC_Decrypt(int keyid, void *iv, const void *in, int len, void *out) Synchronous implementation of IOSC_DecryptAsync 0 on success
6c int check_sig(const void *hash, int hash_length, int signer_keyid, const void *sig) 0 on success
6d int hmac 0 on success
6e int hmac_async 0 on success
6f int validate_cert(void *cert, int signer_keyid, int dest_keyid) 0 on success
70 int get_ng_cert(void *cert) Write 0x180 bytes of NG certificate to cert 0 on success
71 int set_key_permissions(int keyid, u32 mask) Allow the PIDs set in mask to use this key 0 on success
72 int get_key_permissions(int keyid, u32 *mask) Get a mask of the PIDs allowed to use this key 0 on success
73 int get_random_data(void *data, u32 size) Write size bytes of random data to data 0 on success
74 int init_random_key(int keyid) Sets contents of keyid to random data
75 int make_ECC_sig(void *SHAhash, u32 hash_length, int keyid, void *ecc_sig) 0 on success
76 int syscall_76(int ecc_keyid, void *in_64bytes, void *out_180bytes) 0 on success
77 syscall_77 can only be called from DI 0 on success, negative on error
78 syscall_78 can only be called from ES 0 on success, negative on error
79 syscall_79 can only be called from ES negative on error
7a syscall_7a
7b syscall_7b
7c syscall_7c

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 the following syscall is still used in production versions of IOS:

MOVS r0, #syscall_number
SVC 0xAB

Register r0 takes the syscall number. Register r1 takes the first parameter.

ID # Internal name Description Return value
4 write(const char *string) Prints a null-terminated debug message. none.