IOS/Kernel
Kernel
After being parsed by it's own ELF loader, the IOSU's kernel is launched from the Wii U's SRAM (0xFFFF0000). IOSU's kernel follows a standard ARM microkernel architecture:
// IOSU kernel entry-point
// ARM vector table (firmware 5.5.1)
start() // 0xFFFF0000
{
// Reset handler
pc <- sub_FFFF0060
// Undefined handler
pc <- loc_812DD6C
// SWI handler
pc <- sub_812DD20
// Prefetch handler
pc <- sub_812E74C
// Abort handler
pc <- sub_812E720
// NULL
pc <- loc_FFFF0040
// IRQ handler
pc <- loc_FFFF0044
// FIQ handler
pc <- loc_FFFF0048
}
Execution follows into the reset handler:
// Reset handler (firmware 5.5.1)
sub_FFFF0060()
{
r0 <- 0
// Invalidate ICache
MCR p15, 0, R0,c7,c5, 0
// Invalidate DCache
MCR p15, 0, R0,c7,c6, 0
// Read control register
MRC p15, 0, R0,c1,c0, 0
// Set replacement strategy to Round-Robin
r0 <- r0 | 0x1000
// Enable alignment fault checking
r0 <- r0 | 0x2
// Write control register
MCR p15, 0, R0,c1,c0, 0
// Clear the IOS-KERNEL stack
memset_range(stack_start, stack_end, 0, 0x04);
// Disable boot0 mapping
r0 <- 0x0D80018C // HW_SPARE1
r1 <- 0x00(HW_SPARE1)
r1 <- r1 | 0x1000
0x00(HW_SPARE1) <- r1
// MSR CPSR_c, #0xD3 -> Enter supervisor mode, FIQ/IRQ disabled
// SP == 0xFFFF0904
init_svc_stack(0xFFFF0904);
// MSR CPSR_c, #0xD2 -> Enter IRQ mode, FIQ/IRQ disabled
// SP == 0xFFFF1104
init_irq_stack(0xFFFF1104);
// MSR CPSR_c, #0xD1 -> Enter FIQ mode, FIQ/IRQ disabled
// SP == 0xFFFF1504
init_fiq_stack(0xFFFF1504);
// MSR CPSR_c, #0xD7 -> Enter abort mode, FIQ/IRQ disabled
// SP == 0xFFFF0D04
init_abort_stack(0xFFFF0D04);
// MSR CPSR_c, #0xDB -> Enter undefined mode, FIQ/IRQ disabled
// SP == 0xFFFF1904
init_undefined_stack(0xFFFF1904);
// MSR CPSR_c, #0xDF -> Enter system mode, FIQ/IRQ disabled
// SP == 0xFFFF1904
init_user_stack(0xFFFF1904);
// Jump to MEM0 check
lr <- pc
pc <- sub_FFFFDA38
while(1);
}
MEM0's integrity is checked before going any further:
// Check MEM0 for IOS-KERNEL (firmware 5.5.1)
sub_FFFFDA38()
{
r1 <- 0x08143008 // IOSU kernel data region
r3 <- 0xA5A51212
r2 <- 0x00(0x08143008)
// Deadlock
if (0x00(0x08143008) != 0xA5A51212)
{
r3 <- 0xDEAD1111
sp <- 0xDEAD1111
while(1);
}
r12 <- 0xFFFF0500 // IOSU boot heap region
r2 <- 0x00(0xFFFF0500)
// Deadlock
if (0x00(0xFFFF0500) != 0xA5A51212)
{
r3 <- 0xDEAD1111
sp <- 0xDEAD1111
while(1);
}
// Set init magic
r2 <- 0x5A5AEDED
0x00(0xFFFF0500) <- 0x5A5AEDED
0x00(0x08143008) <- 0x5A5AEDED
// Flush AHB for Starbuck
ahbMemFlush(0);
ahb_flush_to(1);
// Load IOS_KERNEL
r2 <- sub_8121B18
sub_8121B18();
// Deadlock
r3 <- 0xDEAD2222
sp <- 0xDEAD2222
while(1);
}
And, finally, execution switches over to MEM0 where the IOS-KERNEL module will be running:
// Load IOS-KERNEL (firmware 5.5.1)
sub_8121B18()
{
// Read LT_DEBUG register
// and store it's value at 0x08150000
sub_8120970();
// Clear LT_DEBUG register
sub_8120988();
// Do a bitwise AND with the LT_DEBUG value
r0 <- 0x80000000
r0 <- sub_81209B8(0x80000000);
// If LT_DEBUG is 0x80000000
// the hardware is using RealView
if (r0 != 0)
{
r4 <- (sp + 0x04)
r3 <- 0
0x00(sp) <- 0
// Wait for user input
// to start the kernel
while (r3 != 0x01)
{
r0 <- "\n\n\n\n\n\n\n\n\n\n"
debug_printf("\n\n\n\n\n\n\n\n\n\n");
r0 <- "Enter '1' to proceed with kernel startup. "
debug_printf("Enter '1' to proceed with kernel startup. ");
r0 <- "%d"
r1 <- sp
debug_read("%d", sp);
r3 <- 0x00(sp)
}
}
// Initialize the MMU and map memory regions
sub_8120C58();
// Reset GPIOs and IRQs
sub_8120510();
// Setup IRQ handlers
sub_8120488();
// Clear and set some kernel structures
sub_812B308();
// Setup iobuf
sub_81235E8();
// Re-map shared_user_ro
sub_81294AC();
// Clear IOS_KERNEL module's thread stack and run it
sub_812CD08();
return;
}
At this point, the IOS-KERNEL module is running on it's own thread and becomes responsible for launching and controlling all the other IOSU modules. The kernel is responsible for tasks such as as IPC handling, permissions' control, resource managers (/dev nodes) and much more.
System calls are handled through the ARM undefined handler and mapped into their respective kernel functions.
Subsystems and Defines
IOSTime
IOSTimeStruct
struct IOSTimeStruct {
u32 microsecond;
u32 second;
}
IOSTimeCalendar
struct IOSTimeCalendar {
u32 year;
u32 month;
u32 day;
u32 hour;
u32 minute;
u32 second;
u32 microsecond;
}
IOSThread
IOSThread
IOSU is able to create and handle up to 0xB4 threads. Each thread has a corresponding internal structure stored in kernel SRAM (0xFFFF4D78).
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x40 | Context |
| 0x40 | 0x4 | Pc |
| 0x44 | 0x4 | Next |
| 0x48 | 0x4 | InitPriority |
| 0x4C | 0x4 | Priority |
| 0x50 | 0x4 | State |
| 0x54 | 0x4 | Pid |
| 0x58 | 0x4 | Tid |
| 0x5C | 0x4 | Attributes |
| 0x60 | 0x4 | ExitValue |
| 0x64 | 0x4 | JoinQueue |
| 0x68 | 0x4 | Queue |
| 0x6C | 0x38 | |
| 0xA4 | 0x4 | Sp |
| 0xA8 | 0x8 | |
| 0xB0 | 0x4 | SysStackBase |
| 0xB4 | 0x4 | UsrStackBase |
| 0xB8 | 0x4 | UsrStackSize |
| 0xBC | 0x4 | IpcBufferPool |
| 0xC0 | 0x4 | ScheduledCount |
| 0xC4 | 0x4 | ScheduledTime |
State
| Value | Description |
|---|---|
| 0 | Available |
| 1 | Ready |
| 2 | Running |
| 3 | Stopped |
| 4 | Waiting |
| 5 | Dead |
| 6 | Faulted |
| 7 | Unknown |
IOSThreadStackInfo
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | SysStackBase |
| 0x4 | 0x4 | SysStackSize |
| 0x8 | 0x4 | SysStackUsedSize |
| 0xC | 0x4 | UsrStackBase |
| 0x10 | 0x4 | UsrStackSize |
| 0x14 | 0x4 | UsrStackUsedSize |
IOSThreadUtilization
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | MaxThreads |
| 0x4 | 0x4 | AllocatedThreads |
| 0x8 | 0x4 | MaxAllocatedThreads |
| 0xC | 0x4 | FaultedThreads |
IOSThreadProfile
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Tid |
| 0x4 | 0x4 | Pid |
| 0x8 | 0x4 | Priority |
| 0xC | 0x4 | State |
| 0x10 | 0x4 | ScheduledCount |
| 0x14 | 0x4 | TotalScheduledCount |
| 0x18 | 0x4 | ScheduledTime |
| 0x1C | 0x4 | TotalScheduledTime |
| 0x20 | 0x4 | |
| 0x24 | 0x4 | Sp |
| 0x28 | 0x4 |
IOSThreadTrace
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Total |
| 0x4 | 0x4 | Idx |
| 0x8 | 0x4 | TimerVal |
| 0xC | 0x4 | TimerBase |
| 0x10 | 0x8 | UpTime |
| 0x18 | 0x4 | Flags |
| 0x1C | 0x1 * 180 | Pids |
IOSContext
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Cpsr |
| 0x4 | 0x4 | R0 |
| 0x8 | 0x4 | R1 |
| 0xC | 0x4 | R2 |
| 0x10 | 0x4 | R3 |
| 0x14 | 0x4 | R4 |
| 0x18 | 0x4 | R5 |
| 0x1C | 0x4 | R6 |
| 0x20 | 0x4 | R7 |
| 0x24 | 0x4 | R8 |
| 0x28 | 0x4 | R9 |
| 0x2C | 0x4 | R10 |
| 0x30 | 0x4 | R11 |
| 0x34 | 0x4 | R12 |
| 0x38 | 0x4 | R13 |
| 0x3C | 0x4 | Lr |
IOSHeap
IOSU is able to create and handle up to 0x30 heaps. Each heap has a corresponding descriptor structure stored in the kernel's BSS section (0x08150008 in firmware 5.5.1).
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Base |
| 0x4 | 0x4 | Owner |
| 0x8 | 0x4 | Size |
| 0xC | 0x4 | FreeList |
| 0x10 | 0x4 | AllocErrors |
| 0x14 | 0x4 | FreeErrors |
| 0x18 | 0x4 | ReAllocErrors |
| 0x1C | 0x4 | InvalidChunks |
| 0x20 | 0x4 | Flags |
| 0x24 | 0x4 | TotalAllocatedSize |
| 0x28 | 0x4 | LargestAllocatedSize |
| 0x2C | 0x4 | AllocatedChunks |
| 0x30 | 0x4 | FreeChunks |
| 0x34 | 0x4 | FreeUnusedChunkErrors |
| 0x38 | 0x4 | AllocInvalidHeapErrors |
| 0x3C | 0x4 | AllocInvalidHeapId |
All accesses to heaps are verified using owner PID and active PID. Heaps are referenced using IDs that are used as indices into the heap descriptor array. There are 3 special heap IDs:
| Value | Description |
|---|---|
| 0x0001 | Shared heap |
| 0xCAFE | Local process heap for active PID |
| 0xCAFF | Cross process heap for active PID |
Access to special heap IDs is redirected to the appropriate heap.
Each process can allocate a cross process heap for multiple processes to use and a local process heap for itself. These are kept tracked of using two arrays following the heap descriptor array in kernel BSS:
int32 local_process_heaps[14]; int32 cross_process_heaps[14];
They are initialized to IOS_ERROR_INVALID within the IOSU kernel and are set to the appropriate heap ID when created using IOS_CreateLocalProcessHeap or IOS_CreateCrossProcessHeap. There may only be one cross/local process heap for each PID.
Each heap descriptor contains a flag field that contains information about the heap:
0x1: Local process heap 0x2: Cross process heap
Each heap is created from memory of the shared heap. It is initialized as one big seperate memory chunk.
When memory is allocated to a heap, the linked list (terminated using nullptr's) is traversed to find a large enough chunk, chunks are split and back and forward pointers are cleared for the allocated chunk. When a chunk is allocated aligned, a chunk bigger than the needed one may be allocated. Inside this chunk, a second heap chunk is set up in a fashion that the beginning of the memory block described by this "inner" chunk is aligned according to the specified alignment. It's magic is set to 0xBABE0002 and the back pointer is set to the chunk containing it. These inner chunks can not be expanded.
IOSChunk
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Status |
| 0x4 | 0x4 | Size |
| 0x8 | 0x4 | PrevFree |
| 0xC | 0x4 | NextFree |
Status
| Value | Description |
|---|---|
| 0xBABE0000 | Chunk is free |
| 0xBABE0001 | Chunks is used |
| 0xBABE0002 | Chunk is inner chunk and used |
IOSHeapProfile
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | Owner |
| 0x4 | 0x4 | Base |
| 0x8 | 0x4 | Size |
| 0xC | 0x4 | FreeChunks |
| 0x10 | 0x4 | LargestChunkSize |
| 0x14 | 0x4 | SmallestChunkSize |
| 0x18 | 0x4 | TotalFreeSize |
| 0x1C | 0x4 | AllocErrors |
| 0x20 | 0x4 | FreeErrors |
| 0x24 | 0x4 | ReAllocErrors |
| 0x28 | 0x4 | CorruptChunks |
| 0x2C | 0x4 | InvalidChunks |
| 0x30 | 0x4 | Flags |