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
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, void *reply) When the call finishes, it sends a message to the queue with the address of reply
29 s32 device_ioctlv_async(s32 fd, u32 request, u32 bytes_in, u32 bytes_out, struct iovec *vector, s32 queue, void *reply) When the call finishes, it sends a message to the queue with the address of reply
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.