IOS/Kernel
The IOS kernel is responsible for dispatching interrupts to processes, handling syscalls, and running the IOSP threads. It is independent of the Wii's specific architecture, as Wii-specific functions such as high-level title launching are provided by ES.
Memory map
The kernel maps basic memory regions immediately after its ahbMemFlush calls in main, and it maps the user regions as the processes are started.
struct TranslatedRegion {
void *physicalAddr;
void *translatedAddr;
u32 size;
u32 domain; // Memory protection domain; must be 0-15
u32 access; // Same as ARM
bool outerInnerWriteback;
}
Default regions (IOS9v778)
| Physical address | Translated address | Size | Domain | Access | Outer/Inner Writeback? |
|---|---|---|---|---|---|
| FFF00000 | FFF00000 | 100000 | 15 | 1 | Yes |
| 13640000 | 13640000 | 20000 | 15 | 1 | Yes |
| 13690000 | 13690000 | 80000 | 15 | 1 | Yes |
| 0D800000 | 0D800000 | D0000 | 15 | 2 | No |
| 00000000 | 00000000 | 4000000 | 15 | 3 | Yes |
| 10000000 | 10000000 | 34000000 | 15 | 3 | Yes |
| 13440000 | 13440000 | 30000 | 15 | 3 | No |
| 13400000 | 13400000 | 20000 | 15 | 3 | Yes |
| 13c40000 | 13c40000 | 80000 | 15 | 3 | Yes |
| 13420000 | 13420000 | 20000 | 15 | 2 | No |
| 13810000 | 13810000 | 280000 | 15 | 3 | Yes |
Domains
| Domain | Process | Notes |
|---|---|---|
| 1 | STM | |
| 9 | SDI | |
| 12 | DI | |
| 13 | FS | |
| 14 | ES | |
| 15 | All | The kernel uses this domain for itself but marks all memory as privileged-only |
Threads
The thread system appears to be based on the DS thread system, but with round-robin scheduling due to the size of IOS. IOS uses the IOS_Thread struct to keep track of a thread.
struct IOS_Context {
u32 psr; // 0x0
u32 r0; // 0x4
u32 r1; // 0x8
u32 r2; // 0xC
u32 r3; // 0x10
u32 r4; // 0x14
u32 r5; // 0x18
u32 r6; // 0x1C
u32 r7; // 0x20
u32 r8; // 0x24
u32 r9; // 0x28
u32 r10; // 0x2C
u32 r11; // 0x30
u32 r12; // 0x34
void *sp; // 0x38
void *lr; // 0x3C
void *pc; // 0x40
}
struct IOS_Thread {
struct IOS_Context persistentCtx; // 0x0
struct IOS_Thread *next; // 0x44
s32 initialPriority; // 0x48
s32 priority; // 0x4C
u32 state; // 0x50 - 0 for a destroyed/uncreated thread, 1 for a thread that is queued to start, 2 for an active thread, 3 for a thread that has not been started, 4 for a blocked thread
u32 processId; // 0x54
bool detached; // 0x58
u32 result; // 0x5C
struct IOS_ThreadQueue joinQueue; // 0x60
struct IOS_ThreadQueue *queue; // 0x64
struct IOS_Context immediateCtx; // 0x68
void *reservedStackPointer; // 0xAC - used as the default stack pointer in IOS28 and newer. Referred to as "Syscall stack" in one of the debugging functions
}
struct IOS_ThreadQueue {
struct IOS_Thread *head;
}
Thread queues in IOS are circular; the scheduler simply rotates the active queue when rescheduling. Each thread is inserted before the first thread with a lower priority, which works for IOS_CreateThread due to it only allowing a lower priority than the current thread, but IOS_SetThreadPriority allows the priority to be increased (but not past the initial priority), causing threads to be shuffled.
Unlike in Revolution OS, where the initial thread is created over the current code with __OSThreadInit, the first IOS thread (IOSP) is created by the reset vector, launching new code with the standard IOS_CreateThread function.
Message queues
Message queue IDs are allocated globally. A total of 255 message queues can be created.
struct IOS_MessageQueue {
IOS_ThreadQueue emptyQ; // 0x0
IOS_ThreadQueue fullQ; // 0x4
u32 owner; // 0x8
u32 count; // 0xC - current messages enqueued
u32 rotation; // 0x10 - first message is at index rotation, last message is at index rotation-1
u32 size; // 0x14 - message capacity
void *ptr; // 0x18
}
Event handlers
The IRQ vector is responsible for sending event handler messages to the assigned message queue.
struct IOS_EventHandler {
IOS_MessageQueue *mq;
u32 msg;
u32 pid;
u32 unknown;
}
Timers
The timer thread uses a single IOS_Timer instance to track itself, but it is part of the circular linked list that the user timers form.
struct IOS_Timer {
u32 time;
u32 repeatInterval;
IOS_MessageQueue *mq;
u32 msg;
u32 pid;
IOS_Timer *next;
IOS_Timer *prev;
}
Memory allocation
Memory allocation is similar to in the IPC library, although the IOS kernel supports 16 heaps instead of 8.
struct IOS_HeapCell {
u16 magic; // 0xbabe
u16 status; // 0 = free, 1 = allocated, 2 = aligned alias for header
u32 size;
struct HeapBlockHeader *prev; // depends on status; status 0 has the previous free block, status 1 has NULL, status 2 has the main block
struct HeapBlockHeader *next; // NULL for anything besides status 0
}
struct IOS_HeapData {
void *base;
u32 owner; // pid of owning process
u32 size;
struct HeapBlockHeader *freeList;
}
When writing an aligned copy of a block, IOS does not check if it overlaps the existing copy; this could potentially be exploited.
IPC
Resource managers are the backends to the devices that can be opened with IOS_Open. The file descriptors returned by IOS_Open are mapped to internal file descriptors generated by the resource manager.
struct IOS_ResourceManager {
char path[0x40];
u32 pathLen;
struct IOS_MessageQueue *messageQueue;
u32 processId;
bool allowPpcAccess; // only exists in IOS28 and above
}
struct IOS_FileDescriptor {
u32 internalFd;
struct IOS_ResourceManager *resourceManager;
}
struct RequestWrapper {
struct IOSRequest request;
struct IOS_MessageQueue callbackQueue;
struct IOSRequest *replyBuf;
u32 owningThread; // shared pool only
bool allocated; // shared pool only
u32 resourceManagerPid;
}