IOS/Syscalls
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 (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!)
Syscall Table
Names starting with IOS_ are official names. The rest are only educated guesses.
The exact numbers seem to vary between IOSes; for example, 0x5a is IOSC_DeleteObject in IOS9. The entire IOSC seems to be offset by 2; LaunchRM is probably one of the two syscalls added later, although the other is not known.
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 JoinThread(int threadid, u32 *returned_value) | Waits for a thread to finish executing | 0 on success | ||||||||||||||||||||
2 | int CancelThread(int threadid, u32 return_value ) | Ends a thread, called automatically when proc returns | 0 on success | ||||||||||||||||||||
3 | int IOS_GetThreadId() | Get the current thread's ID | Current threadid | ||||||||||||||||||||
4 | int GetProcessId() | Get the current process's ID | Current processid | ||||||||||||||||||||
5 | int IOS_StartThread(int threadid) | Resume the specified thread | 0 on success | ||||||||||||||||||||
6 | int SuspendThread(int threadid) | Suspend the specified thread | 0 on success | ||||||||||||||||||||
7 | void YieldThread(void) | Yield execution to any higher priority threads | |||||||||||||||||||||
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(IOSMessage *ptr, u32 n_msgs) | Create a queue at ptr, for n_msgs messages. IOSMessage is a typedef for u32. | The queue ID | ||||||||||||||||||||
b | int IOS_DestroyMessageQueue(int queueid) | Destroy a message queue | 0 on success | ||||||||||||||||||||
c | int IOS_SendMessage(int queueid, IOSMessage message, u32 flags) | Add a message to the end queue | 0 on success | ||||||||||||||||||||
d | int IOS_JamMessage(int queueid, IOSMessage message, u32 flags) | Add a message to the front of a queue | 0 on success | ||||||||||||||||||||
e | int IOS_ReceiveMessage(int queueid, IOSMessage *message, u32 flags) | Fetch a message from the front of a queue | 0 on success | ||||||||||||||||||||
f | int IOS_HandleEvent(int device, int queueid, IOSMessage 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, IOSMessage 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 IOS_CreateHeap(void *ptr, int size) | Create a new heap at ptr of size bytes | heapid or error (negative value) | ||||||||||||||||||||
17 | int IOS_DestroyHeap(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* IOS_AllocAligned(int heapid, u32 size, u32 align) | Allocate size bytes from the specified heap with the requested alignment | pointer to aligned memory | ||||||||||||||||||||
1a | int IOS_Free(int heapid, void *ptr) | Release allocated memory back to the heap | 0 on success | ||||||||||||||||||||
1b | int IOS_RegisterResourceManager(const char* device, int queueid) | Registers device to the device tree, so it can be opened with IOS_Open (from Starlet and Broadway) using the device name. Typically starts with /dev, but / is also registered by FS to allow files to be opened directly. | 0 on success | ||||||||||||||||||||
1c | int IOS_Open(const char* path, int mode) | Generates a file descriptor, and notifies the device if the path points to a device. | The file descriptor | ||||||||||||||||||||
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, IOVector *vector) | Perform the requested IOCTL | Return value from IOCTL | ||||||||||||||||||||
23 | int IOS_OpenAsync(const char* device, int mode, int queueid, IOSRequest *message) | Copy of IOS_Open that writes to a message queue. Unlike the other async IPC syscalls, this is not actually asynchronous, and only differs in the return method. | 0 on success, ipcmessage is sent to the queue with the command's return value | ||||||||||||||||||||
24 | int IOS_CloseAsync(int fd, int queueid, IOSRequest *message) | Async implementation of IOS_Close | 0 on success | ||||||||||||||||||||
25 | int IOS_ReadAsync(int fd, void *buf, u32 len, int queueid, IOSRequest *message) | Async implementation of IOS_Read | |||||||||||||||||||||
26 | int IOS_WriteAsync(int fd, const void *buf, u32 len, int queueid, IOSRequest *message) | Async implementation of IOS_Write | |||||||||||||||||||||
27 | int IOS_SeekAsync(int fd, int offset, int origin, int queueid, IOSRequest *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, IOSRequest *message) | Async implementation of IOS_Ioctl | |||||||||||||||||||||
29 | int IOS_IoctlvAsync(int fd, u32 request, u32 vector_count_in, u32 vector_count_out, IOVector *vector, int queueid, IOSRequest *message) | Async implementation of IOS_Ioctlv | |||||||||||||||||||||
2a | int IOS_ResourceReply( const IOSMessage *request, int retval) | return from a cmd on a resource | |||||||||||||||||||||
2b | int SetUid(u32 pid, u32 uid) | Set the UID for a process (PID <= 0x13). This can only be used from the kernel or ES. | IPC_SUCCESS on success, IPC_EACCES (permission denied if current PID > 0x1) or IPC_EINVAL (invalid PID) | ||||||||||||||||||||
2c | IOSUid GetUid(void) | Get the UID for the active process (based on the thread PID). | UID (u32) | ||||||||||||||||||||
2d | int SetGid(u32 pid, u16 gid) | Set the GID for a process (PID <= 0x13). This can only be used from the kernel or ES. | IPC_SUCCESS on success, IPC_EACCES (permission denied if current PID > 0x1) or IPC_EINVAL (invalid PID) | ||||||||||||||||||||
2e | IOSGid GetGid(void) | Get the GID for the active process (based on the thread PID). | GID (u16) | ||||||||||||||||||||
2f | int AHB_MemFlush(int ahb_dev) | ||||||||||||||||||||||
30 | int AHB_MemRBInvalidate(int ahb_dev)[check] | ||||||||||||||||||||||
31 | int ClearAndEnableIPCIOPIntr(void) | Enables hardware interrupts for device 31 (IPC (Starlet)) (can only be used from the kernel or ES) | 0 on success, -4 for no permission. | ||||||||||||||||||||
32 | int ClearAndEnableDIIntr(void) | Enables hardware interrupts for device 18 (DI) (can only be used from DI) | 0 on success, -4 for no permission | ||||||||||||||||||||
33 | int ClearAndEnableSDIntr(u8 hc) | Enables hardware interrupts for device 7 (SDHC - must be SDI) if hc==0, else device 8 (802.11 Wireless - must be WL) | 0 on success, -4 for no permission | ||||||||||||||||||||
34 | int ClearAndEnableEvent(IOSEvent event) | Enables hardware interrupts for the given device, with PID requirements (this is also used to syscalls 31 through 33):
|
0 on success, -1 for unknown IRQ (not in that table), -4 for no permission | ||||||||||||||||||||
35 | int AccessIobPool(u32 pool) | 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 | Unknown; related to IO buffer functionality | |||||||||||||||||||||
3f | void IOS_InvalidateDCache(void *address, u32 size) | "sync_before_read" - Invalidates dcache, and something (probably related to flushing memory) | |||||||||||||||||||||
40 | void IOS_FlushDCache(const void *address, u32 size) | "sync_after_write" - Flushes dcache and does magic bullshit (aka magic AHB operations). If size is smaller or equal than 0x4000 , it performs CP15 "Clean data cache line (MVA)" (c7, c10, 1 ) on each line, and otherwise, it performs CP15 "Test and clean" (c7, c10, 3 ) |
|||||||||||||||||||||
41 | IOS_StartPPC(const char *path) | Loads a .dol or .elf file into memory and bootstraps the PPC | |||||||||||||||||||||
42 | [[noreturn]] int ios_boot(const char* path, bool suspendBroadway, u32 version) | Suspends the IPC thread, loads a new IOS kernel from the NAND to 0x10100000 in IOS59), then calls boot_new_ios_kernel(0x10100000, version) . This can only be called from UID 0. |
Doesn't return if the boot succeeded; otherwise, an error code is returned. | ||||||||||||||||||||
43 | [[noreturn]] int boot_new_ios_kernel(void* ios_binary_address, u32 new_version) | Sets the version at 0x3140 to new_version and the IPC buffer range ("DDR settings") to the legacy range ("12M"), before branching to the new kernel. This can only be called from UID 0. |
- | ||||||||||||||||||||
44 | int assert_di_reset() | Clears bit 10 (DI) of HW_RESETS (can only be called from DI) | Returns 0 on success, -1 on error | ||||||||||||||||||||
45 | int deassert_di_reset() | Enables bit 10 (DI) of HW_RESETS (can only be called from DI) | Returns 0 on success, -1 on error | ||||||||||||||||||||
46 | bool check_di_reset() | Checks bit 10 (DI) of HW_RESETS (can only be called from DI) | Returns 1 on reset asserted, 0 on deasserted or error | ||||||||||||||||||||
47 | void get_kernel_flavor(u32 *type, u16 *unk) | The implementation of this syscall differs between "flavors" of kernel and returns some identifiers. | The IOSv58 kernel writes (*type=3, *unk=0). The boot2v4 kernel writes (*type=0, *unk_0), although types 0-2 all seem to be reserved for boot2. | ||||||||||||||||||||
48 | void get_unk_flavor(u32 *type, u16 *unk) | Potentially vestigial. Probably related to syscall 0x47 above. | Always returns (*type=1, *unk=0). | ||||||||||||||||||||
49 | u32 get_boot_vector() | Returns a pointer (?) depending on the SRAM mirror bit in HW_MEMIRR. |
Returns 0xffffff00 when the mirror bit is set. Returns 0x0d40ff00 when the mirror bit is unset. | ||||||||||||||||||||
4a | u32 GetHollywoodId(void) | ||||||||||||||||||||||
4b | void kernel_debug_print(u32 flags) | Prints various debug info (depending on flags) from the kernel | |||||||||||||||||||||
4c | int SetLoMemOSVersion(u32 version) | Stores version to 0x3140 (can only be called by ES) | 0 on success | ||||||||||||||||||||
4d | u32 GetLoMemOSVersion(void) | Returns the current IOS version from 0x3140 (can only be called by ES) | IOS version or 0 on error | ||||||||||||||||||||
4e | int SetDiSpinup(u32 s) | Sets or clears the DI_SPIN GPIO; if enable is 0 then the flag is set (disabling spinup); it is cleared otherwise. (Can only be called by DI) | 0 on success, -1 on error | ||||||||||||||||||||
4f | void *VirtualToPhysical(void *virt) | Converts a virtual pointer to its physical equivalent | |||||||||||||||||||||
50 | int SetDvdReadDisable(u8 disable) | Enable/Disable DI DVD Video commands (can only be called from DI) | 0 on success, -1 on error | ||||||||||||||||||||
51 | u32 GetDvdReadDisable(void) | Return status of DI DVD Video commands (can only be called from DI) | 1 if disabled, 0 if enabled or error | ||||||||||||||||||||
52 | int SetEnableAHBPI2DI(u8 enable) | Sets bit 4 of HW_AIPPROT, clearing it if value is 0 and setting it otherwise. DI only seems to call it with false[check]. (Can only be called from DI) | 0 on success, -1 on error | ||||||||||||||||||||
53 | u8 GetEnableAHBPI2DI(void) | Checks bit 4 of HW_AIPPROT (can only be called from DI) | 1 if bit 4 of HW_AIPPROT is set, 0 if not set or error | ||||||||||||||||||||
54 | int SetPPCACRPerms(u8 enable) | Enable/Disable PPC AHBPROT setting (can only be called from ES) | 0 on success, -1 on error | ||||||||||||||||||||
55 | u32 GetBusSpeed(void) | Returns the bus speed in megahertz: 162 in GC mode (if the bottom bit of HW_CLOCKS), and 243 in Wii mode typically, though the Wii value can vary based on HW_PLLSYS and HW_PLLSYSEXT. | |||||||||||||||||||||
56 | int ACRRegWrite(u32 offset, u32 value) | Set gpio reg to value (can only be called from STM) | 0 on success, -1 on error | ||||||||||||||||||||
57 | int DDRRegWrite(u32 offset, u32 value) | Writes a 32-bit value to the specified Memory Controller register. Can only be called from the STM module. | Returns 0 on success, -1 on error. | ||||||||||||||||||||
58 | void OutputDebugPort(u8 value) | Set GPIO lines 16-23 (DEBUG0-7, the debug port) to the provided value. | |||||||||||||||||||||
59 | int SetIpcAccessRights(u8 *rights) | Related to PPC IPC. Can only be called from the ES module. Called when bootstrapping PPC. | 0 on success, negative for error. | ||||||||||||||||||||
5a | int LaunchRM(const char *path) | Load an ARM ELF [IOS module] and start a new thread. | 0 on success, negative for error | ||||||||||||||||||||
5b | IOSCError IOSC_CreateObject(u32* key_handle, IOSCObjectType type, IOSCObjectSubType subtype); | Create a new keyring entry. key_handle is updated with a key handle to use with other IOSC calls. |
0 on success, negative for error | ||||||||||||||||||||
5c | IOSCError IOSC_DeleteObject(u32 key_handle) | Remove a keyring entry | 0 on success, negative for error | ||||||||||||||||||||
5d | IOSCError IOSC_ImportSecretKey(IOSCSecretKeyHandle importedHandle, IOSCSecretKeyHandle verifyHandle, IOSCSecretKeyHandle decryptHandle, IOSCSecretKeySecurity flag, u8 * signbuffer, u8 * ivData, u8 * keybuffer); | Sets the contents of a key handle. This is commonly used to import a built-in key handle (such as the common key). | 0 on success, negative for error | ||||||||||||||||||||
5e | IOSCError IOSC_ExportSecretKey(IOSCSecretKeyHandle exportedHandle, IOSCSecretKeyHandle signHandle, IOSCSecretKeyHandle encryptHandle, IOSCSecretKeySecurity security_flag, u8 * signbuffer, u8 * ivData, u8 * keybuffer); | 0 on success, negative for error | |||||||||||||||||||||
5f | IOSCError IOSC_ImportPublicKey(u8 * publicKeyData, u8 * exponent, IOSCPublicKeyHandle publicKeyHandle); | Sets the contents of a signature. The imported public key must match publicKeyHandle 's type. exponent is optional 4 bytes that can be attached |
0 on success, negative for error | ||||||||||||||||||||
60 | IOSCError IOSC_ExportPublicKey(u8 * publicKeyData, u8 * exponent, IOSCPublicKeyHandle publicKeyHandle); | Gets the contents of a signature | 0 on success, negative for error | ||||||||||||||||||||
61 | IOSCError IOSC_ComputeSharedKey(IOSCSecretKeyHandle privateHandle, IOSCPublicKeyHandle publicHandle, IOSCSecretKeyHandle sharedHandle); | Generates a new AES crypto key (sharedHandle ) from an ecdh shared secret calculated from a sender's ECC key (publicHandle ) and our own ECC key (privateHandle ) |
0 on success, negative for error | ||||||||||||||||||||
62 | IOSCError IOSC_SetData(IOSCDataHandle dataHandle, u32 value); | 0 on success, negative for error | |||||||||||||||||||||
63 | IOSCError IOSC_GetData(IOSCDataHandle dataHandle, u32 * value); | Fetch 4 bytes of userdata from the key | 0 on success (userdata in data), negative for error | ||||||||||||||||||||
64 | IOSCError IOSC_GetKeySize(u32 * keySize, IOSCKeyHandle handle); | Return the key size | 0 on success, negative for error | ||||||||||||||||||||
65 | IOSCError IOSC_GetSignatureSize(u32 * signSize, int handle); | Return the signature size | 0 on success, negative for error | ||||||||||||||||||||
66 | int IOSC_GenerateHashAsync(u8 * context, u8 * inputData, u32 inputSize, u32 chainingFlag, u8 * hashData, int message_queue_id, IOSRequest* reply); | Calculate SHA1 hash of inputData . An IPC reply is sent to the message queue on completion. |
0 on success | ||||||||||||||||||||
67 | IOSCError IOSC_GenerateHash(u8 * context, u8 * inputData, u32 inputSize, u32 chainingFlag, u8 * hashData); | Synchronous implementation of IOSC_GenerateHashAsync | 0 on success | ||||||||||||||||||||
68 | int IOSC_EncryptAsync(IOSCSecretKeyHandle encryptHandle, u8 * ivData, u8 * inputData, u32 inputSize, u8 * outputData, int message_queue_id, IOSRequest* reply) | AES-encrypt inputSize bytes from inputData using encryptHandle and ivData (which gets updated) and write to outputData . An IPC reply is sent to the message queue on completion. |
0 on success | ||||||||||||||||||||
69 | IOSCError IOSC_Encrypt(IOSCSecretKeyHandle encryptHandle, u8 * ivData, u8 * inputData, u32 inputSize, u8 * outputData); | Synchronous implementation of IOSC_EncryptAsync | 0 on success | ||||||||||||||||||||
6a | int IOSC_DecryptAsync(IOSCSecretKeyHandle decryptHandle, u8 * ivData, u8 * inputData, u32 inputSize, u8 * outputData, int message_queue_id, IOSRequest* request); | AES-decrypt inputSize bytes from inputData using decryptHandle and ivData (which gets updated) and write to outputData . An IPC reply is sent to the message queue on completion. |
0 on success | ||||||||||||||||||||
6b | IOSCError IOSC_Decrypt(IOSCSecretKeyHandle decryptHandle, u8 * ivData, u8 * inputData, u32 inputSize, u8 * outputData); | Synchronous implementation of IOSC_DecryptAsync | 0 on success | ||||||||||||||||||||
6c | IOSCError IOSC_VerifyPublicKeySign(u8 * inputData, u32 inputSize, IOSCPublicKeyHandle publicHandle, u8 * signData); | 0 on success | |||||||||||||||||||||
6d | IOSCError IOSC_GenerateBlockMAC(u8 * context, u8 * inputData, u32 inputSize, u8 * customData, u32 customDataSize, IOSCSecretKeyHandle signerHandle, u32 chainingFlag, u8 * signData); | 0 on success | |||||||||||||||||||||
6e | IOSCError IOSC_GenerateBlockMACAsync(u8 * context, u8 * inputData, u32 inputSize, u8 * customData, u32 customDataSize, IOSCSecretKeyHandle signerHandle, u32 chainingFlag, u8 * signData, int message_queue_id, IOSRequest* reply); | Async version of IOSC_GenerateBlockMAC | 0 on success | ||||||||||||||||||||
6f | IOSCError IOSC_ImportCertificate(u8 * certData, IOSCPublicKeyHandle signerHandle, IOSCPublicKeyHandle publicKeyHandle); | 0 on success | |||||||||||||||||||||
70 | IOSCError IOSC_GetDeviceCertificate(IOSCEccSignedCert * certificate); | Write 0x180 bytes of NG certificate to certificate |
0 on success | ||||||||||||||||||||
71 | IOSCError IOSC_SetOwnership(u32 handle, u32 users); | Allow the PIDs set in mask to use this key | 0 on success | ||||||||||||||||||||
72 | IOSCError IOSC_GetOwnership(u32 handle, u32 * users); | Get a mask of the PIDs allowed to use this key | 0 on success | ||||||||||||||||||||
73 | IOSCError IOSC_GenerateRand(u8 * randBytes, u32 numBytes); | Write size bytes of random data to data | 0 on success | ||||||||||||||||||||
74 | IOSCError IOSC_GenerateKey(IOSCKeyHandle handle); | Sets contents of handle to random data
| |||||||||||||||||||||
75 | IOSCError IOSC_GeneratePublicKeySign(u8 * hash, u32 hashLength, IOSCSecretKeyHandle signerHandle, u8 * eccSignature); | Makes an ECC signature | 0 on success | ||||||||||||||||||||
76 | IOSCError IOSC_GenerateCertificate(IOSCSecretKeyHandle privateHandle, IOSCCertName certname, IOSCEccSignedCert * certificate); | 0 on success | |||||||||||||||||||||
77 | IOSCError IOSC_CheckDiHashes(u8 * destAddr, u8 * diskRdBuf, u32 h1Index, u32 h2Index, u8 * h3Ptr); | can only be called from DI | 0 on success, negative on error | ||||||||||||||||||||
78 | IOSError SetProcessPriorities(ProcessPriority *newPriorities, u32 numProcesses) | Updates the process priorities, which adjusts all of the thread priorities. Can only be called by ES. | Returns 0 on success, negative on error | ||||||||||||||||||||
79 | IOSError GetProcessPriorities(ProcessPriority *priorities, u32 numProcesses) | Gets the current process priorities. Can only be called from ES. | Returns 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. |
IOSC built-in key handles
The above crypto commands use key/crypto object handles. These handles can be either from IOSC_CreateObject(which can then be initialized with IOSC_ImportSecretKey in the case of AES), or a built-in handle. The available built-in handles/ids are listed below.
Names starting with IOSC are official names which were found in the GPLed parts of IOS.
ID | Internal name | Description |
---|---|---|
0 | IOSC_DEV_SIGNING_KEY_HANDLE | ECC-233 private key (source: xyzzy) |
1 | IOSC_DEV_ID_HANDLE | Console ID |
2 | IOSC_FS_ENC_HANDLE | NAND AES-128 key |
3 | IOSC_FS_MAC_HANDLE | NAND HMAC |
4 | IOSC_COMMON_ENC_HANDLE | Common key |
5 | IOSC_BACKUP_ENC_HANDLE | PRNG seed (source: xyzzy) |
6 | IOSC_APP_ENC_HANDLE | SD AES-128 key (source: xyzzy) |
7 | IOSC_BOOTOSVER_HANDLE | boot2 version (4 bytes, updated by ES_ImportBoot with the low 32 bits from the TMD IOS title ID field) |
8 | IOSC_CACRLVER_HANDLE | Unknown - Appears to be unused |
9 | IOSC_SIGNERCRLVER_HANDLE | Unknown - Appears to be unused |
10 | IOSC_FSVER_HANDLE | Unknown - Used in the FS driver - SEEPROM NAND generation? |
11 | IOSC_COMMON2_ENC_HANDLE | Korean common key |
Error codes
This list of IOS error codes should be complete for IOS59. Other codes that can technically be returned, but only indirectly (since ES makes use of the FS module and IOSC) are not included in an exhaustive manner in this list.
Error code | Notes |
---|---|
0 | No Error (Success) |
-1 | IPC_EACCES - Permission Denied |
-2 | IPC_EEXIST - File exists |
-3 | IPC_EINTR - Waiting operation was interrupted |
-4 | IPC_EINVAL - Invalid argument |
-5 | IPC_EMAX - Parameter was greater than a max number |
-6 | IPC_ENOENT - Not Found |
-7 | IPC_EQUEUEEMPTY - Queue is empty |
-8 | IPC_EQUEUEFULL - Queue is full |
-9 | IPC_ERETURN - noreturn function returned |
-12 | IPC_EIO - ECC error |
-22 | IPC_ENOMEM - Alloc failed during request |