Difference between revisions of "Revolution OS"
m (REL files are loaded with OSLink. I have no idea if the information remains correct for REL files. RSO files are handled by RsoInit/RsoLoad.) |
Hallowizer (talk | contribs) (→Exceptions: added memory interface "exception") |
||
Line 260: | Line 260: | ||
! Type | ! Type | ||
! ID | ! ID | ||
− | ! | + | ! Vector location |
+ | ! Notes | ||
|- | |- | ||
| System Reset | | System Reset | ||
Line 321: | Line 322: | ||
| 14 | | 14 | ||
| 80001700 | | 80001700 | ||
+ | |- | ||
+ | | Memory interface | ||
+ | | 15 | ||
+ | | N/A | ||
+ | | This is not an exception, but memory IRQs are sent to the unhandled exception function as exception 15. | ||
|} | |} | ||
Revision as of 01:16, 8 August 2022
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 .
|
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 | |
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.
Logging
There appear to be different functions to log debug, info, and error messages; the logging level is controlled by the version of the SDK used, not a global variable. On retail SDKs, info and error are logged, but debug is a dummy function.
It is not known where the logs go.
Exceptions
With the exception of a system call, all exceptions have the same handler, 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 | |
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 | |
Trace | 10 | 80000D00 | |
Performance Monitor | 11 | 80000F00 | |
IABR | 12 | 80001300 | |
Reserved | 13 | 80001400 | |
Thermal | 14 | 80001700 | |
Memory interface | 15 | N/A | This is not an exception, but memory IRQs are sent to the unhandled exception function as exception 15. |
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.