Revolution OS

From WiiBrew
Jump to navigation Jump to search

Revolution OS is a part of the SDK that is always included. It does not handle security; IOS is responsible for that.

Public functions

Name Prototype Description
OSGetConsoleType int OSGetConsoleType(void) Returns the platform ID. See below for more details.
OSInit void OSInit(void) Initializes all parts of the library.
OSSaveFPUContext void OSSaveFPUContext(OSContext *ctx) Saves the floating point registers to ctx.
OSSetCurrentContext void OSSetCurrentContext(OSContext *ctx) Sets the context to ctx, causing future register saving to go there.
OSGetCurrentContext OSContext *OSGetCurrentContext(void) Gets the OSContext instance currently being used (not the floating point one).
OSSaveContext bool OSSaveContext(OSContext *ctx) Saves nonessential registers to ctx, since essential registers were saved by the exception vector if needed. Returns true if the function is returning after ctx was reloaded back to the state, which was saved as being in this function.
OSLoadContext void OSLoadContext(OSContext *ctx) Loads the registers in ctx. This function is also responsible for ensuring OSDisableInterrupts runs atomically to prevent MSR corruption.
OSGetStackPointer void *OSGetStackPointer(void) Returns the current stack pointer.
OSSwitchFiber void OSSwitchFiber(void *code, void *stack) Swaps the stack and jumps to code.
OSSwitchFiberEx void OSSwitchFiberEx(int param1, int param2, int param3, int param4, void *code, void *stack) Swaps the stack and jumps to code, passing up to 4 args.
OSClearContext void OSClearContext(OSContext *ctx) Marks ctx as having nothing saved to it.
OSInitContext void OSInitContext(OSContext *ctx) Sets all saved registers in ctx to 0.
OSDumpContext void OSDumpContext(OSContext *ctx) Prints all saved registers in ctx.
OSReport void OSReport(char *format, ...) Calls vprintf; the formatted text gets sent through some MetroTRK-related SerialIO port
OSPanic void OSPanic(char *sourceFile, int lineNo, char *format, ...) Prints the message similar to OSReport, then prints the crash location and a stack trace.
OSDisableInterrupts bool OSDisableInterrupts(void) Disables interrupts, allowing code to run atomically. Returns the previous interrupt state for a call to OSRestoreInterrupts.
OSEnableInterrupts bool OSEnableInterrupts(void) Enables interrupts, returning the previous interrupt state. This function should be used with care, since it may have unintended effects if a calling function disabled interrupts.
OSRestoreInterrupts bool OSRestoreInterrupts(bool interruptState) Sets the interrupt state to the state specified by the parameter. Typically used at the end of an atomic segment. Returns the previous interrupt state.
__OSSetInterruptHandler void (*OSSetInterruptHandler(int interrupt, void (*handler)(void)))(void) Sets the handler for a particular interrupt, returning the old handler.
__OSGetInterruptHandler void (*OSGetInterruptHandler(int interrupt))(void) Reads the appropriate handler from a table.
OSInitMessageQueue void OSInitMessageQueue(OSMessageQueue *queue, int *buf, int capacity) Initializes the fields of the OSMessageQueue
OSSendMessage bool OSSendMessage(OSMessageQueue *queue, int msg, bool waitForSpace) Adds a message to the end of the queue. Returns whether this was successful.
OSReceiveMessage bool OSReceiveMessage(OSMessageQueue *queue, int *msg, bool waitForMsg) Removes a message from the front of the queue, returning whether the operation was successful.
OSJamMessage bool OSJamMessage(OSMessageQueue *queue, int msg, bool waitForSpace) Adds a message to the front of the queue. Returns whether this was successful.
OSGetPhysicalMem1Size int OSGetPhysicalMem1Size(void) Reads the MEM1 size from lomem.
OSGetPhysicalMem2Size int OSGetPhysicalMem2Size(void) Reads the MEM2 size from lomem.
OSGetConsoleSimulatedMem1Size int OSGetConsoleSimulatedMem1Size(void) Reads the simulated MEM1 size from lomem.
OSGetConsoleSimulatedMem2Size int OSGetConsoleSimulatedMem2Size(void) Reads the simulated MEM2 size from lomem.
OSInitMutex void OSInitMutex(OSMutex *mutex) Initializes the fields of a mutex
OSLockMutex void OSLockMutex(OSMutex *mutex) Locks a mutex, blocking if needed. Supports recursive locking.
OSUnlockMutex void OSUnlockMutex(OSMutex *mutex) Unlocks a mutex. Supports recursive locking.
OSTryLockMutex bool OSTryLockMutex(OSMutex *mutex) Attempts to lock a mutex, returning whether the operation was successful.
OSYieldThread void OSYieldThread(void) Switches to another thread
OSCreateThread bool OSCreateThread(OSThread *thread, void (*main)(void *arg), void *arg, void *stackPtr, int stackSize, int priority, bool detached) Creates and starts a thread using the given OSThread.
OSCancelThread void OSCancelThread(OSThread *thread) Stops a thread
OSSleepThread void OSSleepThread(OSThreadQueue *waitingQueue) Pauses the current thread until OSResumeThread is called with this thread queue
OSResumeThread void OSResumeThread(OSThreadQueue *waitingQueue) Resumes all threads in the queue

OSGetConsoleType

OSInit calls a function to log platform information, which gets the platform ID from a function called OSGetConsoleType.

OSGetConsoleType priorities the masked DVD device code address first, MEM2 size second, and Hollywood size third. If an entry in the table is marked with an X, then any value can work, unless that value matches an explicitly listed console type.

Name ID (hex) Hollywood version (hex) Masked DVD device code address (hex) MEM2 size Notes
Pre-production board 1 00000011 1 2
3
203
64MB
Pre-production board 2-1 00000012 2 2
3
203
64MB
Pre-production board 2-2 00000020 10 2
3
203
64MB
RVA 1 00000100 X 300 X This did not exist in launch day versions of OSGetConsoleType.
Retail # 0XXXXXXX X 2
3
203
X Always followed by the ID
NDEV 2.1 10000021 11 202 128MB This does not exist in the June 23 2006 version of Revolution OS (found in MIOS), but it does exist in the July 27 2006 version (found in NandIOS2 in RVL_DIAG), which still has support for RVL0 apploaders.
NDEV 2.0 10000020 10 202 128MB
NDEV 1.2 10000012 2 202 128MB
NDEV 1.1 10000011 1 202 128MB
NDEV 1.0 10000010 0 202 128MB
Revolution Emulator 10000008 XXXXXXXX X X
Emulation platform (#) 1XXXXXXX X 202 X Always printed with the console type ID.
TDEV-based emulation HW# 2XXXXXXX X X X Always printed with the console type ID.

OSDBIntegrator

There is a string saying Installing OSDBIntegrator. OSDBIntegrator seems to be a debug stub installed at 0x80000060 that branches to the address stored at 0x80000048. It is not known how this code is reached, as it is not jumped to by any retail code.

The code itself seems to be printing DBExceptionDestination, printing the current OSContext, and hanging the Broadway.

Exceptions

With the exception of a system call, all exceptions have identical vectors, which loads the appropriate exception-specific handler from the array at 0x80003000, or the default handler if the is no specific handler or the exception is not recoverable. Exception types are given IDs by Revolution OS; these numbers are used when setting an exception handler.

Type ID Vector location Notes
System Reset 0 80000100 Hard resets boot from the EXI buffer, and the boot vector placed by IOS boots to 80003400. As a result, only soft resets can be handled here.
Machine Check 1 80000200
DSI 2 80000300
ISI 3 80000400
External (IRQ) 4 80000500
Alignment 5 80000600
Program 6 80000700
FP unavailable 7 80000800
Decrementer 8 80000900
System call 9 80000C00 This exception cannot be handled unless __OSInitSystemCall (normally called by OSInit) does not execute, since the system call vector is overwritten.
Trace 10 80000D00
Performance Monitor 11 80000F00
IABR 12 80001300
Reserved 13 80001400
Thermal 14 80001700
Memory interface 15 N/A Memory IRQs are sent to this exception handler. Unlike other exception handlers, this should be NULL to indicate that it should be unhandled (not a pointer to the default handler).

IRQs

IRQs are processed by handlers in the __OSInterrupt table; the bit index of the each source is the same as the index in the table; integers built from these bits are used for functions that mask interrupts.

Index Source
0 MEM0
1 MEM1
2 MEM2
3 MEM3
4 All MI
5 DSP ADINT
6 DSP ARINT
7 DSP DSPINT
8 AI
9 EXI EXTINT low (channel 0)
10 EXI TCINT (channel 0)
11 EXI EXTINT high (channel 0)
12 EXI EXTINT low (channel 1)
13 EXI TCINT (channel 1)
14 EXI EXTINT high (channel 1)
15 EXI EXTINT low (channel 2)
16 EXI TCINT (channel 2)
26 HSP
25 DEBUG
20 PE FINISH
19 PE TOKEN
24 VI
21 Serial
22 DVD
23 Reset switch
18 CP FIFO
27 GP Runtime Error

Context saving

When a non-syscall exception occurs, the current state is stored in a struct called OSContext; the struct is 0x2c8 bytes long.

struct OSContext {
	u32 gprs[0x20]; // r0-r31
	u32 cr; // 0x80
	u32 lr; // 0x84
	u32 ctr; // 0x88
	u32 xer; // 0x8c
	u64 fprs[0x20]; // f0-f31
	u64 fpscr; // 0x190
	u32 srr0; // 0x198 - saved PC
	u32 srr1; // 0x19c - saved MSR
	u16 state; // 0x1a2; last bit means OSSaveFPUContext was called, second last bit means the GPRs were saved by the exception handler
	u64 gqrs[4]; // 0x1a4
	u64 pairedSingles[0x20]; // starting at 0x1c8
}

Most OSContext instances belong to threads, and are at the beginning of the OSThread struct. However, there are also some standalone OSContext instances, such as the one used while waiting for an available thread.

FPU

Revolution OS keeps the FPU disabled by default, and enables it when an FPU unavailable exception triggers. When other exceptions trigger, Revolution OS disables the FPU in the previous OSContext.

Threads

Threads are stored in the OSThread struct.

struct OSThread {
	struct OSContext ctx;
	u16 state; // 0x2c8; 0 = stopped, 1 = inactive, 2 = active, 4 = sleeping, 8 = returned result?
	u16 detached; // 0x2ca; zero = false, nonzero = true
	u32 suspend; // seems to be a balancing counter. 0 = active, 1 = suspended
	u32 priority; // 0x2d0; can range from 0-31
	u32 basePriority; // 0x2d4
	u32 returnValue; // 0x2d8
	struct OSThreadQueue *queue; // 0x2dc
	struct OSThreadLink linkQueue; // 0x2e0
	struct OSThreadQueue queueJoin; // 0x2e8
	struct OSMutex *mutex; // 0x2f0; mutex currently waiting for; used for deadlock detection
	struct OSMutexQueue queueMutex; // 0x2f4
	struct OSThreadLink linkActive; // 0x2fc
	void *stackStart; // 0x304
	void *stackEnd; // 0x308
	u32 unknown; // 0x30c
	u32 threadSpecifics[2]; // 0x310
}

struct OSThreadQueue {
	struct OSThread *head;
	struct OSThread *tail;
}

struct OSThreadLink {
	struct OSThread *next;
	struct OSThread *prev;
}

struct OSMutex {
	struct OSThreadQueue waitingQueue;
	struct OSThread *holder;
	u32 timesLocked; // used if a mutex is locked multiple times by the same thread
	struct OSMutex *next;
	struct OSMutex *prev;
}

struct OSMutexQueue {
	struct OSMutex *head;
	struct OSMutex *tail;
}

struct OSThreadInfo { // fields are sometimes directly accessed
	struct OSThread initialThread;
	struct OSThreadQueue RunQueue[0x20];
	struct OSContext idleCtx;
}

When rescheduling, threads are taken from the queue corresponding to the smallest priority. Additionally, unless the rescheduling is done by OSYieldThread, the new priority must be smaller than the current one for a thread switch to occur.

Memory allocation

Memory can be allocated from a heap or the MEM1 arena.

struct OSHeapCell {
	struct OSHeapCell *prev;
	struct OSHeapCell *next;
	u32 size;
	u8 unknown[0x14];
}

struct OSHeapData {
	u32 size;
	struct OSHeapCell *free;
	struct OSHeapCell *allocated;
}

Message queues

Message queues are very similar to IOS, but with no process isolation.

struct OSMessageQueue {
	struct OSThreadQueue waitForSend;
	struct OSThreadQueue waitForReceive;
	void *buf;
	u32 messageCapacity;
	u32 rotation;
	u32 messagesEnqueued;
}

Shutdown handlers

Shutdown handlers only seem to be called when an error occurs, not on normal shutdown or return to System Menu.

struct OSShutdownFunction {
	void *func; // 2 params, unknown type
	u32 priority; // lower priority goes first
	struct OSShutdownFunction *next;
	struct OSShutdownFunction *prev;
}

Dynamic linking

REL files are loaded through OSLink. At each relocation address, the instruction is patched with information about the base address depending on the relocation type. Types 1, 3, and 4 are identical and are used for 32-bit data, while type 2 is used for absolute branching instructions, type 5 is used for 16-bit data, type 6 is used for swapped endian, types 7-9 are used for absolute conditional branches, type 10 is used for relative branches, and types 11-13 are used for relative conditional branches. There is also a type 109 (0x6d - 'm') that has an unknown purpose.

__OSModuleInit writes NULL pointers to the module linked list head and tail, but no code in main binaries seems to write real pointers here; this is presumably done by the modules themselves.