IOS/Syscalls
This is an old revision of this page, as edited by Waninkoko (talk | contribs) at 15:33, 22 August 2010. It may differ significantly from the current revision. |
This article is a stub. You can help WiiBrew 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)
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 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, =literal_2 kernel:FFFF1F70 E7 9B B1 0A LDR R11, [R11,R10,LSL#2] kernel:FFFF1F74 E0 8D D1 0B ADD SP, SP, R11,LSL#2 ; SP += R11[R10 << 2] kernel:FFFF1F78 unknown_loop ; 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,#var_4]! 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 unknown_loop 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 kernel:FFFF1FDC E5 8D E0 50 STR LR, [SP,#arg_50] 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 | u32 thread_create( u32 (*proc)(void* arg), u8 priority, u32* stack, u32 stacksize, void* arg, BOOL autostart) | Creates a thread | Returns threadid |
1 | thread_join | ||
2 | thread_cancel( u32 threadid, u32 ? ) | ||
3 | get_tid | ||
4 | get_pid | ||
5 | thread_continue | ||
6 | thread_stop( u32 threadid ) | ||
7 | thread_yield | ||
8 | thread_get_priority | ||
9 | thread_set_priority | ||
a | s32 message_queue_create(void *ptr, int n_msgs) | Create a queue at ptr, for n_msgs messages | The queue ID |
b | message_queue_destroy(int queue) | ||
c | message_queue_send | ||
d | message_queue_send_now | ||
e | message_queue_receive(int queue, void *message, int flags) | ||
f | RegisterEventHandler(int device, int queue, int message) | ||
10 | UnregisterEventHandler | ||
11 | IOS_CreateTimer(int time, int wtf, int message_queue, int message) | ||
12 | IOS_RestartTimer | ||
13 | IOS_StopTimer | ||
14 | IOS_DestroyTimer | ||
15 | timer_now | ||
16 | heap_create(void *ptr, int size) | ||
17 | heap_destroy(int heap) | ||
18 | heap_alloc(int heap, int size) | ||
19 | heap_alloc_aligned(int heap, int size, int align) | ||
1a | heap_free(void *ptr) | ||
1b | BOOL device_register(char* device, u32 messagequeue) | Registers device to the device tree, so it can be opened (from Starlet and PPC) | Returns 0 on success, else error |
1c | s32 device_open(char* device, int mode) | Similar to IOS_Open on PPC, except now internal to the IOS system | Returns an fd |
1d | s32 device_close(s32 fd) | ||
1e | s32 device_read(s32 fd, void *buf, s32 len) | ||
1f | s32 device_write(s32 fd, void *buf, s32 len) | ||
20 | s32 device_seek(s32 fd, s32 where, s32 whence) | ||
21 | s32 device_ioctl(s32 fd, u32 request, void *input_buffer, u32 input_buffer_len, void *output_buffer, u32 output_buffer_len) | ||
22 | s32 device_ioctlv(s32 fd, u32 request, u32 bytes_in, u32 bytes_out, struct iovec *vector) | ||
23 | s32 device_open_async(char* device, int mode, void *callback) | ||
24 | s32 device_close_async(s32 fd, void *callback) | ||
25 | s32 device_read_async(s32 fd, void *buf, s32 len, void *callback) | ||
26 | s32 device_write_async(s32 fd, void *buf, s32 len, void *callback) | ||
27 | s32 device_seek_async(s32 fd, s32 where, s32 whence, void *callback) | ||
28 | s32 device_ioctl_async(s32 fd, u32 request, void *input_buffer, u32 input_buffer_len, void *output_buffer, u32 output_buffer_len, s32 queue, s32 *reply) | When the call finishes, it sends a message to the queue with the address of reply | Result stored in reply[1] |
29 | s32 device_ioctlv_async(s32 fd, u32 request, u32 bytes_in, u32 bytes_out, struct iovec *vector, s32 queue, s32 *reply) | When the call finishes, it sends a message to the queue with the address of reply | Result stored in reply[1] |
2a | int IOS_ResourceReply( struct ios_resource_request *request, int retval) | return from a cmd on a resource | |
2b | SetUID | ||
2c | get_hmac_queue_for_pid | ||
2d | SetGID | ||
2e | lookup_GID_maybe | ||
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(u32 address, u32 size) | Invalidates dcache, and something (probably related to flushing memory) | |
40 | sync_after_write(u32 address, u32 size) | Flushes dcache and does magic bullshit (aka magic AHB operations) | |
41 | ppc_boot | ||
42 | ios_boot | ||
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 | kernel_debug_print | ||
4c | kernel_set_version | ||
4d | kernel_get_version | ||
4e | poke_E0_1 | ||
4f | virt_to_phys(void *ptr) | ||
50 | u32 init_video(int) | ||
51 | syscall_51 | ||
52 | syscall_52 | ||
53 | syscall_53 | allows Broadway to access otherwise protected hardware (e.g. SD, NAND,...) directly. can only be called from kernel context | |
54 | syscall_54 | ||
55 | GetBUSClock | Returns either 162(GC) or 243(Wii) | |
56 | poke_gpios | ||
57 | syscall_57 | ||
58 | call_poke_debug_port | ||
59 | create_key | ||
5a | destroy_key | ||
5b | keyring_allocate_entry(int *index, char usage, char algorithm) | ||
5c | keyring_deallocate_entry(int index) | ||
5d | set_public_key (7 arguments) | ||
5e | crypto_syscall_5e (7 arguments) | ||
5f | keyring_set_keystore_data(char *data, u32 *offset_0x0c, int index) | sets the keystore data for an entry in the keyring. if the second param is not NULL, it sets it to the u32 at keyring_entry+0xc | |
60 | crypto_syscall_60 | ||
61 | get_keyid(int keyring_index_key, int keyring_index_sig, int keyring_index_output) | ||
62 | crypto_syscall_62 | ||
63 | get_key | Used to get entries from the keyring. R0 is key index: | |
0 ECC Private Key | |||
1 Console ID | |||
2 NAND AES Key | |||
3 NAND HMAC | |||
4 Common Key | |||
5 PRNG Seed (unused?) | |||
6 SD Key | |||
7 Boot2 version | |||
8 ? | |||
9 ? | |||
10 Filesystem metadata (SFFS) generation | |||
11 "Korean Common Key" | |||
64 | sha_async | ||
65 | sha | ||
66 | aes_async (7 args) | ||
67 | aes (5 args) | ||
68 | crypto_syscall_68 (7 args) | ||
69 | crypto_syscall_69 (5 args) | ||
6a | crypto_syscall_6a (7 args) | ||
6b | aes_decrypt(int keyid, void *iv, void *in, int len, void *out) | ||
6c | hmac_async | ||
6d | crypto_syscall_6d (8 args) | ||
6e | get_ng_cert (10 args) | ||
6f | key_set_permission_mask | ||
70 | crypto_syscall_70 | ||
71 | crypto_syscall_71 | ||
72 | crypto_syscall_72 | ||
73 | crypto_syscall_73 | ||
74 | crypto_syscall_74 | ||
75 | crypto_syscall_75 | ||
76 | crypto_syscall_76 | ||
77 | ? | ||
78 | ? | ||
79 | ? | ||
7A | ? |
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. |