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; }