From WiiBrew
Jump to navigation Jump to search

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


Domain Process Notes
12 DI
13 FS
14 ES
15 All The kernel uses this domain for itself but marks all memory as privileged-only


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;


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.


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;