IOS/Kernel

From WiiUBrew
< IOS
Jump to navigation Jump to search

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