SceExcpmgr: Difference between revisions

From Vita Development Wiki
Jump to navigation Jump to search
 
(15 intermediate revisions by 3 users not shown)
Line 32: Line 32:
<source lang="C">
<source lang="C">
// All data in this structure are per-core (field_name[0] belongs to CPU0, field_name[1] to CPU1, etc)
// All data in this structure are per-core (field_name[0] belongs to CPU0, field_name[1] to CPU1, etc)
typedef struct _DBGBVC_save {
SceUInt32 unk_0x00;
SceUInt32 unk_0x04;
SceUInt32 DBGBVR0;
SceUInt32 DBGBCR0;
SceUInt32 DBGBVR1;
SceUInt32 DBGBCR1;
SceUInt32 DBGBVR2;
SceUInt32 DBGBCR2;
SceUInt32 DBGBVR3;
SceUInt32 DBGBCR3;
SceUInt32 DBGBVR4;
SceUInt32 DBGBCR4;
} DBGBVC_save;
typedef struct _DBGWVC_save {
SceUInt32 unk_0x00;
SceUInt32 unk_0x04;
SceUInt32 DBGWVR0;
SceUInt32 DBGWCR0;
SceUInt32 DBGWVR1;
SceUInt32 DBGWCR1;
SceUInt32 DBGWVR2;
SceUInt32 DBGWCR2;
SceUInt32 DBGWVR3;
SceUInt32 DBGWCR3;
} DBGWVC_save;
typedef struct _SceExcpmgrCB {
typedef struct _SceExcpmgrCB {
    // Incremented by the first exception handler, must be 1 when then decremented before returning from an exception.
// Incremented by the first exception handler, must be 1 when then decremented before returning from an exception.
    // If not, raise a nested exception kernel panic.
// If not, raise a nested exception kernel panic.
    SceUInt32 nestedExceptionCount[4];
SceUInt32 nestedExceptionCount[4];
    int unused[4]; // Seemingly unused
int unused[4]; // Seemingly unused
    void *ExcpStackTop[4]; // Stack size is 0x1000
void *ExcpStackTop[4]; // Stack size is 0x1000
    void *ExcpStackBottom[4];
void *ExcpStackBottom[4];
void *unk_0x40;
DBGBVC_save *pDBGBVC_save;
DBGWVC_save *pDBGWVC_save;
} SceExcpmgrCB;
} SceExcpmgrCB;


Line 183: Line 215:
} SceExcpmgrRegistersSave;
} SceExcpmgrRegistersSave;
</source>
</source>
== Built-in handlers behaviour ==
SceExcpmgr is mostly responsible for providing a facility that allows installing exception handlers to other modules. However, it also installs exception handlers by itself.
SceExcpmgr registers an infinite loop as the default exception handler in <code>module_start</code>. For RESET, SVC, RESERVED and IRQ, another infinite loop handler will be installed at priority 7.
SceExcpmgr also installs a priority 7 handler for PABT, DABT and UNDEF. As specified in the [[SceExcpmgr#Types|Types section]], those handlers will panic the kernel if they receive 
either <code>SCE_EXCPMGR_EXCEPTION_NOT_HANDLED_FATAL</code> or <code>SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_4</code> as their handling code parameter, infinite loop if nested exception count is greater than 1, and call [[SceSysmem#sceKernelSysrootReturnFromExcpToThreadForKernel|sceKernelSysrootReturnFromExcpToThreadForKernel]] otherwise.
In addition to this, SceExcpmgr installs a priority 0 handler for PABT, DABT and UNDEF.
Those three have a common behaviour: building the <code>SceExcpmgrExceptionContext</code>, incrementing the nested exception count, clearing some CP14 control registers if needed, loading the <code>SceProcessContext</code> provided by [[SceSysmem#SceSysrootForKernel_118657C6|SceSysrootForKernel_118657C6]], setting DACR to 0x15450000 (not done in System Software version 0.990.030), zero'ing out <code>TPIDRPRW</code> and <code>TPIDRURO</code>, and calling a per-handler callback. The CP14 registers that get zero'ed out are the breakpoint control registers (<code>DBGBCR0</code>-<code>DBGBCR5</code>) if a non-NULL pointer has been provided to [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], and the watchpoint control registers (<code>DBGWCR0</code>-<code>DBGWCR3</code>) if a non-NULL pointer has been provided to [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]].
=== PABT handler ===
The PABT handler performs no additional work and merely transfers control to the next exception handler in chain.
=== UNDEF handler ===
In System Software version 0.990.030, the UNDEF handler performs no additional work and merely transfers control to the next exception handler in chain.
Since System Software version ?.??, the UNDEF handler will check if the instruction that caused the exception is a <code>MCRR p15, 0, Rt, Rt2, c11</code>, in which case it will be replaced with a <code>NOP</code> before execution is resumed at the faulting instruction (which is now a <code>NOP</code>).
Otherwise, control is transferred to the next exception handler in chain. According to the ARM Cortex-A9 TRM, this MCRR instruction is used to program the ''Preload Engine'' channels.
=== DABT handler ===
The DABT handler is responsible for handling aborts in the [[SceSysmem#sceKernelGetVmaccessRangeForKernel|Vmaccess routines]].
If such an exception has not happened, control is transferred to the next exception handler in chain.
==== Old Vmaccess model ====
In System Software revision 0.931.010 and earlier, the Vmaccess functions follow this pattern:
<source lang="armasm">
CopyFromUser32:
    ldrt r2, [r0] @or any load from kernel/user
    str r2, [r1]  @or any store to kernel/user
    mov r0, 0    @Return value = SCE_OK
    cpy r0, r0
    cpy r0, r0    @nop
    bx lr
</source>
If an invalid (user) pointer was provided to this function, a Data Abort would occur on the checked instruction (in this case, <code>ldrt</code>).
When an exception occurs, if the CPU is in ARM mode and <code>pc</code> belongs in the old model Vmaccess range, the DABT handler will set <code>r0 = 0x80022005</code> (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS) in the exception context then resume execution 'three instructions after the faulting one', in the <code>nop</code>sled (in modern firmwares, only one instruction is skipped instead of 3).
The caller thus see the function failed as it returned an error code (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS).
In recent firmwares, the old Vmaccess range must be explicitely set via a call to [[SceExcpmgr#SceExcpmgrForKernel_C45C0D3D|SceExcpmgrForKernel_C45C0D3D]] instead of being fetched during the call to [[SceSysmem#sceKernelGetVmaccessRangeForKernel|sceKernelGetVmaccessRange]].
==== New Vmaccess model ====
Since System Software revision 0.940, this model has been progressively phased out and replaced with a new Vmaccess model.
Under the new model, the Vmaccess functions all begin by loading the address of a function in <code>r12</code> but no longer have to follow a specific pattern.
When an exception occurs, if the CPU is in ARM mode and the instruction at <code>pc</code> is a checked memory access instruction (i.e. one of {<code>strt</code>, <code>strht</code>, <code>strbt</code>, <code>ldrt</code>, <code>ldrht</code>, <code>ldrbt</code>, <code>ldrsht</code>, <code>strsbt</code>}), execution is resumed at the address pointed to by <code>r12</code>. The address loaded in <code>r12</code> points to a snippet that cleans the stack if needed then returns SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS.
Note that if the old and new model ranges overlap, the latter takes priority.
This means that exceptions raised from code residing in the overlap will be handled under the new model.
If the DABT or UNDEF handlers return, CP14 registers are restored from either the global variables set previously by [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]] and [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], or interrupted thread's TPIDRPRW->0x18->0xC if the TPIDRPRW is non-NULL. If no breakpoint restore structure has been provided to [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], then watchpoints will not be restored regardless of whether something was provided to [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]] or not.
== Non-exported functions ==
The following table contains offsets (from the base of <code>.text</code>/segment 0) to non-exported functions whose name is known:
{| class="wikitable"
|-
! Function name
! 0.990.030
! 3.600.011 CEX
|-
| sceKernelDefaultHandlerCfunc
| 0x2138
| 0x1D4C
|-
| sceKernelDefaultHandlerResetCfunc
| 0x21A8
| 0x1D50
|-
| sceKernelDefaultHandlerSvcCfunc
| 0x21D0
| 0x1D54
|-
| sceKernelDefaultHandlerReservedCfunc
| 0x2238
| 0x1D58
|-
| sceKernelDefaultHandlerIrqCfunc
| 0x2260
| 0x1D5C
|-
| sceKernelPanicHandlerUndefCfunc
| 0x2288
| 0x1D60
|-
| sceKernelPanicHandlerPabtCfunc
| 0x22C4
| 0x1D78
|-
| sceKernelPanicHandlerDabtCfunc
| 0x2304
| 0x1D90
|}
== SceExcpmgrForKernel ==
=== sceKernelRegisterExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x03499636
|-
| 3.65 || 0x00063675
|}
Temp name was sceExcpmgrRegisterHandlerForKernel.
Installs an exception handler. The exception handler can be ARM code or Thumb code. Allowed <code>priority</code> values are from 0 (most important) to 7 (least important), including them. Specifying priority 0 will install the handler directly in VBAR, which means its code will run directly when an exception raises (and as such it needs to build the exception context itself).
<code>handler & 0x1</code> must be set if the handler function is Thumb code.
The syscall handler in FW 1.50 is SceExcpmgr_func_0x81000E40.
The syscall handler in FW 3.65 is located at offset 0xF40 in [[SceKernelIntrMgr]]'s <code>.text</code> segment.
<source lang="C">int sceKernelRegisterExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelRegisterDefaultExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xFFCCB5F9
|-
| 3.65 || 0x416C0E20
|}
Installs an exception handler for all exceptions. If no exception handler is already installed for an exception, the handler will be installed in the VBAR. <code>handler</code> must be the same parameter that would be passed to [[SceExcpmgr#sceKernelRegisterExceptionHandlerForKernel|sceKernelRegisterExceptionHandlerForKernel]].
<source lang="C">int sceKernelRegisterDefaultExceptionHandlerForKernel(SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelReleaseExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xE04AE286
|-
| 3.65 || 0xDA7BB671
|}
Uninstalls an exception handler.
Pass the same arguments as provided to [[SceExcpmgr#sceKernelRegisterExceptionHandlerForKernel|sceKernelRegisterExceptionHandlerForKernel]].
<source lang="C">int sceKernelReleaseExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelReleaseDefaultExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990 || 0x917A6D2B
|-
| 3.60 || not present
|}
=== sceKernelInitialHandlerDebugUndefCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xB77DCBD6
|-
| 3.65 || 0x64A057C7
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugUndefCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelInitialHandlerDebugPabtCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0x25C0C91B
|-
| 3.65 || 0x64A057C7
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugPabtCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelInitialHandlerDebugDabtCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xFFFA3353
|-
| 3.65 || 0xD195E55C
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugDabtCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelPanicHandlerReturnFromNestedExceptionCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xD17EEE40
|-
| 3.65 || 0x3E55B5C3
|}
This name is probably incorrect.
<source lang="C">void sceKernelPanicHandlerReturnFromNestedExceptionCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== SceExcpmgrForKernel_A90AC525 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xA90AC525
|-
| 3.65 || 0x4337DD78
|}
Returns a pointer to the handler previously registered by [[SceExcpmgr#sceKernelRegisterDefaultExceptionHandlerForKernel|sceKernelRegisterDefaultExceptionHandlerForKernel]].
<source lang="C">SceExcpmgrExceptionHandlerContext *SceExcpmgrForKernel_A90AC525(void);</source>
=== sceKernelGetExcpStackBottomForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x4C603645
|-
| 3.65 || 0x5420ED8F
|}
Returns the exception stack bottom for specified CPU core. The stack bottom is the highest address of the stack.
Reimplementation code:
<source lang="C">
void* sceKernelGetExcpStackBottomForKernel(int coreNum) {
    return sceKernelGetExcpDataForKernel()->ExcpStackBottom[coreNum];
}
</source>
<source lang="C">void* sceKernelGetExcpStackBottomForKernel(int coreNum);</source>
=== sceKernelGetExcpCBForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x08CB30E6
|-
| 3.65 || 0x96C2869C
|}
This is a guessed name. Temp name was sceKernelGetExcpDataForKernel, sceExcpmgrGetDataForKernel.
Get exception data pointer (for use by handlers).
<source lang="C">SceExcpmgrCB *sceKernelGetExcpCBForKernel(void);</source>
=== SceExcpmgrForKernel_3AE9AEE1 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x3AE9AEE1
|-
| 3.65 || 0x1FBF5654
|}
Sets a callback function ran by the default UNDEF exception handler before calling the next handler in the chain.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_3AE9AEE1(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_4FF90618 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x4FF90618
|-
| 3.65 || 0xE7487AFD
|}
Called by [[SceProcessmgr]] during <code>CreateKernelProcess</code>.
Sets the structure CP14 watchpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See [[SceExcpmgr#Provided handlers' behaviour]] for more information.
<source lang="C">void SceExcpmgrForKernel_4FF90618(void* a1);</source>
=== SceExcpmgrForKernel_A66DDFA3 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xA66DDFA3
|-
| 3.65 || 0x293DFA04
|}
Called by [[SceProcessmgr]] during <code>CreateKernelProcess</code>.
Sets the structure CP14 breakpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See [[SceExcpmgr#Provided handlers' behaviour]] for more information.
<source lang="C">void SceExcpmgrForKernel_A66DDFA3(void* a1);</source>
=== SceExcpmgrForKernel_7ADF11DB ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || Not present
|-
| 3.60 || 0x7ADF11DB
|-
| 3.65 || 0x9EE59C6E
|}
Sets a callback function ran by the default DABT exception handler before trying to handle the exception.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_7ADF11DB(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_8D223205 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x8D223205
|-
| 3.65 || 0x58F7212B
|}
Sets a callback function ran by the default PABT exception handler before calling the next handler in the chain.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_8D223205(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_C45C0D3D ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || Not present
|-
| 3.60 || 0xC45C0D3D
|-
| 3.65 || 0x44CE04B8
|}
Sets the "memory access error area" range. See [[SceExcpmgr#Built-in handlers behaviour|the built-in handlers behaviour section]] for more information.
In older firmwares (0.990.030), this range is acquired during the call to [[SceSysmem#SceCpuForKernel_9A3281C0|SceCpuForKernel_9A3281C0]]
in SceExcpmgr <code>module_start</code> instead.
<source lang="C">void SceExcpmgrForKernel_C45C0D3D(SceUIntPtr memErrorAreaStart, SceUIntPtr memErrorAreaEnd);</source>
=== SceExcpmgrForKernel_D464A9A7 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xD464A9A7
|-
| 3.65 || 0xB615A7DA
|}
Returns the current nested exception count, or 0 if current TPIDRPRW is non-NULL.
<source lang="C">int SceExcpmgrForKernel_D464A9A7(void);</source>
== SceExcpmgrForTZS ==
=== SceExcpmgrForTZS_get_default_excp_handler ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x07A5790B
|}
<source lang="C">void *SceExcpmgrForTZS_get_default_excp_handler(void);</source>
=== sceKernelReleaseExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x166C9362
|}
<source lang="C">int sceKernelReleaseExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelReleaseDefaultExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x6282E52C
|}
<source lang="C">int sceKernelReleaseDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelRegisterDefaultExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0xA0434735
|}
<source lang="C">int sceKernelRegisterDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelRegisterMonitorEntryForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xAC297406
|}
<source lang="C">int sceKernelRegisterMonitorEntryForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelRegisterExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0xDD4C680D
|}
<source lang="C">int sceKernelRegisterExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);</source>


== Exceptions ==
== Exceptions ==
Line 658: Line 236:
void *handler[8];
void *handler[8];
} SceKernelVbaseResetVector;
} SceKernelVbaseResetVector;
</source>
=== Context ===
If you handle with priority 0 these contexts are not applied.
<source>
CPACR <- 0xF00000
fpexc <- 0x40000000
DBGBCR0 <- 0
DBGBCR1 <- 0
DBGBCR2 <- 0
DBGBCR3 <- 0
DBGBCR4 <- 0
DBGBCR5 <- 0
DBGWCR0 <- 0
DBGWCR1 <- 0
DBGWCR2 <- 0
DBGWCR3 <- 0
CONTEXTIDR <- 0
TTBR1 <- ?
CONTEXTIDR <- ? (could be restore)
DACR <- 0x15450000
TPIDRPRW <- 0
TPIDRURO <- 0
</source>
</source>


=== Registered exception handler by PSP2 Kernel ===
=== Registered exception handler by PSP2 Kernel ===


{| class="wikitable"
{| class="wikitable mw-collapsible mw-collapsed"
|-
|-
! excode
! excode
! priority
! priority
! module
! module
! description
! descrption
|-
|-
| rowspan="2" | Reset (0)
| rowspan="2" | Reset (0)
Line 687: Line 295:
| 2
| 2
| SceDeci4pSDfMgr
| SceDeci4pSDfMgr
| -
| Decide whether to return to the excp point or continue with the exception after calling some callback.
|-
|-
<!-- | Undef (1) -->
<!-- | Undef (1) -->
Line 697: Line 305:
| 5
| 5
| SceDeci4pDbgp
| SceDeci4pDbgp
| -
| Save information about the exception and continue with the exception.
|-
|-
<!-- | Undef (1) -->
<!-- | Undef (1) -->
Line 737: Line 345:
| 2
| 2
| SceDeci4pSDfMgr
| SceDeci4pSDfMgr
| -
| Decide whether to return to the excp point or continue with the exception after calling some callback.
|-
|-
<!-- | Pabt (3) -->
<!-- | Pabt (3) -->
Line 747: Line 355:
| 5
| 5
| SceDeci4pDbgp
| SceDeci4pDbgp
| -
| Save information about the exception and continue with the exception.<br>Also, if the crashed address is a specific instruction, some callbacks will be called.
|-
|-
<!-- | Pabt (3) -->
<!-- | Pabt (3) -->
Line 772: Line 380:
| 2
| 2
| SceDeci4pSDfMgr
| SceDeci4pSDfMgr
| -
| Decide whether to return to the excp point or continue with the exception after calling some callback.
|-
|-
<!-- | Dabt (4) -->
<!-- | Dabt (4) -->
Line 782: Line 390:
| 5
| 5
| SceDeci4pDbgp
| SceDeci4pDbgp
| -
| Save information about the exception and continue with the exception.
|-
|-
<!-- | Dabt (4) -->
<!-- | Dabt (4) -->
Line 911: Line 519:
|
|
| <source lang="C">int AllocSharedMemory_S(void *pPA, SceSize size);</source>
| <source lang="C">int AllocSharedMemory_S(void *pPA, SceSize size);</source>
|
| <code>SCE_MONCALL_SET_SHARED_MEMORY</code> - Sets the physical address of the shared memory area for Secure kernel
|-
|-
| 0x103
| 0x103
|
|
| <source lang="C">int FreeSharedMemory_S(void);</source>
| <source lang="C">int FreeSharedMemory_S(void);</source>
| Frees the allocation of virtual ranges: <code>0x40000000</code>-<code>0x40100000</code>, <code>0x50000000</code>-<code>0x51000000</code> and <code>0x51000000</code>-<code>0x52000000</code> with UID 0x10007. Called by SysStateMgr when "''Unmapping Secure KBL''". This is SKBL only and does not affect NSKBL.
| <code>SCE_MONCALL_UNMAP_KBL</code> - Frees the allocation of virtual ranges <code>0x40000000</code>-<code>0x40100000</code>, <code>0x50000000</code>-<code>0x51000000</code> and <code>0x51000000</code>-<code>0x52000000</code> with UID 0x10007 in Secure state kernel. This does not affect Non-Secure state kernel. Called by SysStateMgr when "''Unmapping Secure KBL''".
|-
|-
| 0x104
| 0x104
Line 995: Line 603:
| 0x118
| 0x118
| [[SceDriverTzs]]
| [[SceDriverTzs]]
| <source lang="c">int smc_0x118(void);</source>
| <source lang="c">int smc_cdram_enter_self_resresh(void);</source>
| Seems to enable something CDRAM related.
| Enter CDRAM self-refresh mode. The <code>resresh</code> typo is in SCE name.
|-
|-
| 0x119
| 0x119
| [[SceDriverTzs]]
| [[SceDriverTzs]]
| <source lang="c">int smc_0x119(void);</source>
| <source lang="c">int smc_cdram_exit_self_resresh(void);</source>
| Seems to disable something CDRAM related.
| Exit CDRAM self-refresh mode. The <code>resresh</code> typo is in SCE name.
|-
|-
| 0x11A
| 0x11A
Line 1,020: Line 628:
| 0x11D
| 0x11D
| [[SceDriverTzs]]
| [[SceDriverTzs]]
| <source lang="c">int smc_compat_set_memory_bank_start_paddr(int bank, void *pPA);</source>
| <source lang="c">int sceGrabSetCmpMap(int bank, void *pPA);</source>
| Valid banks: 0-7, and physical addresses: <code>0x20000000</code>-<code>0x2FFFFFFF</code> and <code>0x42800000</code>-<code>0x7FFFFFFF</code>.
| Valid banks: 0-7, and physical addresses: <code>0x20000000</code>-<code>0x2FFFFFFF</code> and <code>0x42800000</code>-<code>0x7FFFFFFF</code>.
|-
|-
Line 1,045: Line 653:
| 0x122
| 0x122
| [[SceDriverTzs]]
| [[SceDriverTzs]]
| <source lang="c">int smc_intr_crash(int type, void *pTTBR0, void *pVBAR, void *pSysroot);</source>
| <source lang="c">int sceCrashDumpMonitorCall(int type, void *pTTBR0, void *pVBAR, void *pSysroot);</source>
| type: 0 (exception), 1 (panic_or_assertion_failed), 2 (bus error)
| type: 0 (exception), 1 (panic_or_assertion_failed), 2 (bus error)
|-
|-
| 0x12D
| 0x12D
|
|
| <source lang="C">int sceSblSmSchedInvoke(SceBool priority, void *sm_self_paddr, SceUInt32 num_pa_range, SceSblTzsBridgeArgArea area);</source>
| <source lang="C">int sceSblSmSchedInvokeMonitorCall(SceBool priority, void *sm_self_paddr, SceUInt32 num_pa_range, SceSblTzsBridgeArgArea area);</source>
| Used by [[SceSblSmschedProxy#sceSblSmSchedProxyInvokeForKernel|sceSblSmSchedProxyInvokeForKernel]].
| Used by [[SceSblSmschedProxy#sceSblSmSchedProxyInvokeForKernel|sceSblSmSchedProxyInvokeForKernel]].
|-
|-
Line 1,141: Line 749:
|
|
| <source lang="c">int smc_l2_write_control_register(int value);</source>
| <source lang="c">int smc_l2_write_control_register(int value);</source>
| Flushes and invalidates the entire L2 Cache (Clean and Invalidate by Way with <code>way = 0xFFFF</code>, offset <code>0x7FC</code>), then performs an L2 Cache Sync (offset <code>0x730</code>), and finally writes <code>value</code> to the L2 Control Register (offset <code>0x100</code>).
| Flushes and invalidates the entire L2 Cache (Clean and Invalidate by Way with <code>way = 0xFFFF</code>, offset <code>0x7FC</code>), then performs an L2 Cache Sync (offset <code>0x730</code>), and finally writes <code>value</code> to the L2 Control Register (offset <code>0x100</code>). Maybe related to PL310 Erratum 588369 (<code>Clean and invalidate maintenance operations do not invalidate clean lines</code>).
|-
|-
| 0x16B
| 0x16B
Line 1,162: Line 770:


FIQs are only handled in secure world because of the bit set in the SCR. Because of this, it is likely that secure devices such as [[Cmep]] use FIQs to communicate with the Cortex A9 cores. See [[SceKernelIntrMgr]].
FIQs are only handled in secure world because of the bit set in the SCR. Because of this, it is likely that secure devices such as [[Cmep]] use FIQs to communicate with the Cortex A9 cores. See [[SceKernelIntrMgr]].
== Built-in handlers behaviour ==
SceExcpmgr is mostly responsible for providing a facility that allows installing exception handlers to other modules. However, it also installs exception handlers by itself.
SceExcpmgr registers an infinite loop as the default exception handler in <code>module_start</code>. For RESET, SVC, RESERVED and IRQ, another infinite loop handler will be installed at priority 7.
SceExcpmgr also installs a priority 7 handler for PABT, DABT and UNDEF. As specified in the [[SceExcpmgr#Types|Types section]], those handlers will panic the kernel if they receive 
either <code>SCE_EXCPMGR_EXCEPTION_NOT_HANDLED_FATAL</code> or <code>SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_4</code> as their handling code parameter, infinite loop if nested exception count is greater than 1, and call [[SceSysmem#sceKernelSysrootReturnFromExcpToThreadForKernel|sceKernelSysrootReturnFromExcpToThreadForKernel]] otherwise.
In addition to this, SceExcpmgr installs a priority 0 handler for PABT, DABT and UNDEF.
Those three have a common behaviour: building the <code>SceExcpmgrExceptionContext</code>, incrementing the nested exception count, clearing some CP14 control registers if needed, loading the <code>SceProcessContext</code> provided by [[SceSysmem#SceSysrootForKernel_118657C6|SceSysrootForKernel_118657C6]], setting DACR to 0x15450000 (not done in System Software version 0.990.030), zero'ing out <code>TPIDRPRW</code> and <code>TPIDRURO</code>, and calling a per-handler callback. The CP14 registers that get zero'ed out are the breakpoint control registers (<code>DBGBCR0</code>-<code>DBGBCR5</code>) if a non-NULL pointer has been provided to [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], and the watchpoint control registers (<code>DBGWCR0</code>-<code>DBGWCR3</code>) if a non-NULL pointer has been provided to [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]].
=== PABT handler ===
The PABT handler performs no additional work and merely transfers control to the next exception handler in chain.
=== UNDEF handler ===
In System Software version 0.990.030, the UNDEF handler performs no additional work and merely transfers control to the next exception handler in chain.
Since System Software version ?.??, the UNDEF handler will check if the instruction that caused the exception is a <code>MCRR p15, 0, Rt, Rt2, c11</code>, in which case it will be replaced with a <code>NOP</code> before execution is resumed at the faulting instruction (which is now a <code>NOP</code>).
Otherwise, control is transferred to the next exception handler in chain. According to the ARM Cortex-A9 TRM, this MCRR instruction is used to program the ''Preload Engine'' channels.
=== DABT handler ===
The DABT handler is responsible for handling aborts in the [[SceSysmem#sceKernelGetVmaccessRangeForKernel|Vmaccess routines]].
If such an exception has not happened, control is transferred to the next exception handler in chain.
==== Old Vmaccess model ====
In System Software revision 0.931.010 and earlier, the Vmaccess functions follow this pattern:
<source lang="armasm">
CopyFromUser32:
    ldrt r2, [r0] @or any load from kernel/user
    str r2, [r1]  @or any store to kernel/user
    mov r0, 0    @Return value = SCE_OK
    cpy r0, r0
    cpy r0, r0    @nop
    bx lr
</source>
If an invalid (user) pointer was provided to this function, a Data Abort would occur on the checked instruction (in this case, <code>ldrt</code>).
When an exception occurs, if the CPU is in ARM mode and <code>pc</code> belongs in the old model Vmaccess range, the DABT handler will set <code>r0 = 0x80022005</code> (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS) in the exception context then resume execution 'three instructions after the faulting one', in the <code>nop</code>sled (in modern firmwares, only one instruction is skipped instead of 3).
The caller thus see the function failed as it returned an error code (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS).
In recent firmwares, the old Vmaccess range must be explicitely set via a call to [[SceExcpmgr#SceExcpmgrForKernel_C45C0D3D|SceExcpmgrForKernel_C45C0D3D]] instead of being fetched during the call to [[SceSysmem#sceKernelGetVmaccessRangeForKernel|sceKernelGetVmaccessRange]].
==== New Vmaccess model ====
Since System Software revision 0.940, this model has been progressively phased out and replaced with a new Vmaccess model.
Under the new model, the Vmaccess functions all begin by loading the address of a function in <code>r12</code> but no longer have to follow a specific pattern.
When an exception occurs, if the CPU is in ARM mode and the instruction at <code>pc</code> is a checked memory access instruction (i.e. one of {<code>strt</code>, <code>strht</code>, <code>strbt</code>, <code>ldrt</code>, <code>ldrht</code>, <code>ldrbt</code>, <code>ldrsht</code>, <code>strsbt</code>}), execution is resumed at the address pointed to by <code>r12</code>. The address loaded in <code>r12</code> points to a snippet that cleans the stack if needed then returns SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS.
Note that if the old and new model ranges overlap, the latter takes priority.
This means that exceptions raised from code residing in the overlap will be handled under the new model.
If the DABT or UNDEF handlers return, CP14 registers are restored from either the global variables set previously by [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]] and [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], or interrupted thread's TPIDRPRW->0x18->0xC if the TPIDRPRW is non-NULL. If no breakpoint restore structure has been provided to [[SceExcpmgr#SceExcpmgrForKernel_A66DDFA3|SceExcpmgrForKernel_A66DDFA3]], then watchpoints will not be restored regardless of whether something was provided to [[SceExcpmgr#SceExcpmgrForKernel_4FF90618|SceExcpmgrForKernel_4FF90618]] or not.
== Non-exported functions ==
The following table contains offsets (from the base of <code>.text</code>/segment 0) to non-exported functions whose name is known:
{| class="wikitable"
|-
! Function name
! 0.990.030
! 3.600.011 CEX
|-
| sceKernelDefaultHandlerCfunc
| 0x2138
| 0x1D4C
|-
| sceKernelDefaultHandlerResetCfunc
| 0x21A8
| 0x1D50
|-
| sceKernelDefaultHandlerSvcCfunc
| 0x21D0
| 0x1D54
|-
| sceKernelDefaultHandlerReservedCfunc
| 0x2238
| 0x1D58
|-
| sceKernelDefaultHandlerIrqCfunc
| 0x2260
| 0x1D5C
|-
| sceKernelPanicHandlerUndefCfunc
| 0x2288
| 0x1D60
|-
| sceKernelPanicHandlerPabtCfunc
| 0x22C4
| 0x1D78
|-
| sceKernelPanicHandlerDabtCfunc
| 0x2304
| 0x1D90
|}
== SceExcpmgrForKernel ==
=== sceKernelRegisterExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x03499636
|-
| 3.65 || 0x00063675
|}
Temp name was sceExcpmgrRegisterHandlerForKernel.
Installs an exception handler. The exception handler can be ARM code or Thumb code. Allowed <code>priority</code> values are from 0 (most important) to 7 (least important), including them. Specifying priority 0 will install the handler directly in VBAR, which means its code will run directly when an exception raises (and as such it needs to build the exception context itself).
<code>handler & 0x1</code> must be set if the handler function is Thumb code.
The syscall handler in FW 1.50 is SceExcpmgr_func_0x81000E40.
The syscall handler in FW 3.65 is located at offset 0xF40 in [[SceKernelIntrMgr]]'s <code>.text</code> segment.
<source lang="C">int sceKernelRegisterExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelRegisterDefaultExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xFFCCB5F9
|-
| 3.65 || 0x416C0E20
|}
Installs an exception handler for all exceptions. If no exception handler is already installed for an exception, the handler will be installed in the VBAR. <code>handler</code> must be the same parameter that would be passed to [[SceExcpmgr#sceKernelRegisterExceptionHandlerForKernel|sceKernelRegisterExceptionHandlerForKernel]].
<source lang="C">int sceKernelRegisterDefaultExceptionHandlerForKernel(SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelReleaseExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xE04AE286
|-
| 3.65 || 0xDA7BB671
|}
Uninstalls an exception handler.
Pass the same arguments as provided to [[SceExcpmgr#sceKernelRegisterExceptionHandlerForKernel|sceKernelRegisterExceptionHandlerForKernel]].
<source lang="C">int sceKernelReleaseExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelReleaseDefaultExceptionHandlerForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990 || 0x917A6D2B
|-
| 3.60 || not present
|}
=== sceKernelInitialHandlerDebugUndefCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xB77DCBD6
|-
| 3.65 || 0x64A057C7
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugUndefCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelInitialHandlerDebugPabtCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0x25C0C91B
|-
| 3.65 || 0x64A057C7
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugPabtCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelInitialHandlerDebugDabtCfuncForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xFFFA3353
|-
| 3.65 || 0xD195E55C
|}
Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.
<source lang="C">void sceKernelInitialHandlerDebugDabtCfuncForKernel(SceExcpmgrExceptionContext* context);</source>
=== sceKernelReturnFromExceptionForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xD17EEE40
|-
| 3.65 || 0x3E55B5C3
|}
This is a guessed name.
<source lang="C">void sceKernelReturnFromExceptionForKernel(SceExcpmgrExceptionContext *context);</source>
=== SceExcpmgrForKernel_A90AC525 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xA90AC525
|-
| 3.65 || 0x4337DD78
|}
Returns a pointer to the handler previously registered by [[SceExcpmgr#sceKernelRegisterDefaultExceptionHandlerForKernel|sceKernelRegisterDefaultExceptionHandlerForKernel]].
<source lang="C">SceExcpmgrExceptionHandlerContext *SceExcpmgrForKernel_A90AC525(void);</source>
=== sceKernelGetExcpStackBottomForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x4C603645
|-
| 3.65 || 0x5420ED8F
|}
Returns the exception stack bottom for specified CPU core. The stack bottom is the highest address of the stack.
Reimplementation code:
<source lang="C">
void* sceKernelGetExcpStackBottomForKernel(int coreNum) {
    return sceKernelGetExcpDataForKernel()->ExcpStackBottom[coreNum];
}
</source>
<source lang="C">void* sceKernelGetExcpStackBottomForKernel(int coreNum);</source>
=== sceKernelGetExcpCBForKernel ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x08CB30E6
|-
| 3.65 || 0x96C2869C
|}
This is a guessed name. Temp name was sceKernelGetExcpDataForKernel, sceExcpmgrGetDataForKernel.
Get exception data pointer (for use by handlers).
<source lang="C">SceExcpmgrCB *sceKernelGetExcpCBForKernel(void);</source>
=== SceExcpmgrForKernel_3AE9AEE1 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x3AE9AEE1
|-
| 3.65 || 0x1FBF5654
|}
Sets a callback function ran by the default UNDEF exception handler before calling the next handler in the chain.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_3AE9AEE1(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_4FF90618 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0x4FF90618
|-
| 3.65 || 0xE7487AFD
|}
Called by [[SceProcessmgr]] during <code>CreateKernelProcess</code>.
Sets the structure CP14 watchpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See [[SceExcpmgr#Provided handlers' behaviour]] for more information.
<source lang="C">void SceExcpmgrForKernel_4FF90618(DBGWVC_save *a1);</source>
=== SceExcpmgrForKernel_A66DDFA3 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.990-3.60 || 0xA66DDFA3
|-
| 3.65 || 0x293DFA04
|}
Called by [[SceProcessmgr]] during <code>CreateKernelProcess</code>.
Sets the structure CP14 breakpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See [[SceExcpmgr#Provided handlers' behaviour]] for more information.
<source lang="C">void SceExcpmgrForKernel_A66DDFA3(DBGBVC_save *a1);</source>
=== SceExcpmgrForKernel_7ADF11DB ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || Not present
|-
| 3.60 || 0x7ADF11DB
|-
| 3.65 || 0x9EE59C6E
|}
Sets a callback function ran by the default DABT exception handler before trying to handle the exception.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_7ADF11DB(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_8D223205 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || not present
|-
| 3.60 || 0x8D223205
|-
| 3.65 || 0x58F7212B
|}
Sets a callback function ran by the default PABT exception handler before calling the next handler in the chain.
<source lang="C">
// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_8D223205(void (*callback)(SceExcpmgrExceptionContext*));
</source>
=== SceExcpmgrForKernel_C45C0D3D ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-0.990 || Not present
|-
| 3.60 || 0xC45C0D3D
|-
| 3.65 || 0x44CE04B8
|}
Sets the "memory access error area" range. See [[SceExcpmgr#Built-in handlers behaviour|the built-in handlers behaviour section]] for more information.
In older firmwares (0.990.030), this range is acquired during the call to [[SceSysmem#SceCpuForKernel_9A3281C0|SceCpuForKernel_9A3281C0]]
in SceExcpmgr <code>module_start</code> instead.
<source lang="C">void SceExcpmgrForKernel_C45C0D3D(SceUIntPtr memErrorAreaStart, SceUIntPtr memErrorAreaEnd);</source>
=== SceExcpmgrForKernel_D464A9A7 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xD464A9A7
|-
| 3.65 || 0xB615A7DA
|}
Returns the current nested exception count, or 0 if current TPIDRPRW is non-NULL.
<source lang="C">int SceExcpmgrForKernel_D464A9A7(void);</source>
== SceExcpmgrForTZS ==
=== SceExcpmgrForTZS_get_default_excp_handler ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x07A5790B
|}
<source lang="C">void *SceExcpmgrForTZS_get_default_excp_handler(void);</source>
=== sceKernelReleaseExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x166C9362
|}
<source lang="C">int sceKernelReleaseExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelReleaseDefaultExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0x6282E52C
|}
<source lang="C">int sceKernelReleaseDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelRegisterDefaultExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0xA0434735
|}
<source lang="C">int sceKernelRegisterDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)</source>
=== sceKernelRegisterMonitorEntryForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-3.60 || 0xAC297406
|}
<source lang="C">int sceKernelRegisterMonitorEntryForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);</source>
=== sceKernelRegisterExceptionHandlerForTZS ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.931-1.80 || 0xDD4C680D
|}
<source lang="C">int sceKernelRegisterExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);</source>


[[Category:ARM]]
[[Category:ARM]]

Latest revision as of 18:18, 15 June 2024

SceExcpmgr is a kernel module that sets up exception handling. A version exists in both non-secure and secure worlds. In non-secure world, after the kernel is booted up, the exception handlers pointed to by VBAR all jump into code in this module.

Module

This module exists in both non-secure and secure world. The non-secure world SELF can be found in os0:kd/excpmgr.skprx.

Version World Privilege
0.931.010-3.740.011 Non-secure Kernel
0.931.010-1.69 Secure Kernel

Libraries

Known NIDs

Version Name World Visibility NID
0.931.010-3.610.011 SceExcpmgrForKernel Non-secure Kernel 0x4CA0FDD5
3.630.011-3.740.011 SceExcpmgrForKernel Non-secure Kernel 0x1496A5B5
0.931.010-1.80 SceExcpmgrForTZS Secure Kernel 0x8F526F35

Types

// All data in this structure are per-core (field_name[0] belongs to CPU0, field_name[1] to CPU1, etc)

typedef struct _DBGBVC_save {
	SceUInt32 unk_0x00;
	SceUInt32 unk_0x04;
	SceUInt32 DBGBVR0;
	SceUInt32 DBGBCR0;
	SceUInt32 DBGBVR1;
	SceUInt32 DBGBCR1;
	SceUInt32 DBGBVR2;
	SceUInt32 DBGBCR2;
	SceUInt32 DBGBVR3;
	SceUInt32 DBGBCR3;
	SceUInt32 DBGBVR4;
	SceUInt32 DBGBCR4;
} DBGBVC_save;

typedef struct _DBGWVC_save {
	SceUInt32 unk_0x00;
	SceUInt32 unk_0x04;
	SceUInt32 DBGWVR0;
	SceUInt32 DBGWCR0;
	SceUInt32 DBGWVR1;
	SceUInt32 DBGWCR1;
	SceUInt32 DBGWVR2;
	SceUInt32 DBGWCR2;
	SceUInt32 DBGWVR3;
	SceUInt32 DBGWCR3;
} DBGWVC_save;

typedef struct _SceExcpmgrCB {
	// Incremented by the first exception handler, must be 1 when then decremented before returning from an exception.
	// If not, raise a nested exception kernel panic.
	SceUInt32 nestedExceptionCount[4];
	int unused[4]; // Seemingly unused
	void *ExcpStackTop[4]; // Stack size is 0x1000
	void *ExcpStackBottom[4];
	void *unk_0x40;
	DBGBVC_save *pDBGBVC_save;
	DBGWVC_save *pDBGWVC_save;
} SceExcpmgrCB;

// Exception handlers' prototype
typedef void(SceExcpmgrExceptionHandler)(SceExcpmgrExceptionContext* context, SceExcpmgrExceptionHandlingCode code); //Prototype for non-zero priority handlers
typedef void(SceExcpmgrPriority0ExceptionHandler)(void); //Prototype for priority 0 handlers

// Parameter for the RegisterHandler/ReleaseHandler functions
typedef struct __attribute__((aligned(4))) _SceExcpmgrExceptionHandlerContext {
    struct _SceExcpmgrExceptionHandlerContext *next; // Pointer to the next handler in the chain
    SceUInt32 must_be_zero; // Unused - probably required for alignement reasons
    // Code of the exception handler goes here.
} SceExcpmgrExceptionHandlerContext;

typedef struct SceArmWaypoint { //!< Size is 0x10-bytes
	SceUInt32 flags;
	SceUIntPtr prev_inst; // pc
	SceUIntPtr curr_inst; // target
	SceUInt32 event;
} SceArmWaypoint;

// Structure that holds the CPU's state at the moment when an exception was raised
typedef struct _SceExcpmgrExceptionContext { //!< Size is 0x400 on FW 3.60
	SceUInt32 r0;
	SceUInt32 r1;
	SceUInt32 r2;
	SceUInt32 r3;
	SceUInt32 r4;
	SceUInt32 r5;
	SceUInt32 r6;
	SceUInt32 r7;
	SceUInt32 r8;
	SceUInt32 r9;
	SceUInt32 r10;
	SceUInt32 r11;
	SceUInt32 r12;
	SceUInt32 sp;
	SceUInt32 lr;
	SceUInt32 pc; // address of faulting instruction (+4)
	SceExcpmgrExceptionCode excode;
	SceUInt32 SPSR;
	SceUInt32 CPACR;
	SceUInt32 FPSCR;
	SceUInt32 FPEXC;
	SceUInt32 CONTEXTIDR;
	SceUInt32 TPIDRURW;
	SceUInt32 TPIDRURO;
	SceUInt32 TPIDRPRW;
	SceUInt32 TTBR1;
	SceUInt32 unused68;
	SceUInt32 DACR;
	SceUInt32 DFSR;
	SceUInt32 IFSR;
	SceUInt32 DFAR;
	SceUInt32 IFAR;
	SceUInt32 PAR;
	SceUInt32 TEEHBR;
	SceUInt32 PMCR;
	SceUInt32 PMCNTENSET;
	SceUInt32 PMCNTENSET_2; //<! Second copy of PMCNTENSET
	SceUInt32 PMSELR;
	SceUInt32 PMCCNTR;
	SceUInt32 PMUSERENR;
	SceUInt32 PMXEVTYPER0;
	SceUInt32 PMXEVCNTR0;
	SceUInt32 PMXEVTYPER1;
	SceUInt32 PMXEVCNTR1;
	SceUInt32 PMXEVTYPER2;
	SceUInt32 PMXEVCNTR2;
	SceUInt32 PMXEVTYPER3;
	SceUInt32 PMXEVCNTR3;
	SceUInt32 PMXEVTYPER4;
	SceUInt32 PMXEVCNTR4;
	SceUInt32 PMXEVTYPER5;
	SceUInt32 PMXEVCNTR5;
	SceUInt32 unusedD0;
	SceUInt32 unkD4; 		//<! Comes from SceVfpIntRegs memblock
	SceUInt32 DBGSCRext;
	SceUInt32 unusedDC[9];  
	SceUInt64 VFP_registers[32]; //<! Content of floating-point registers d0-d31
	SceArmWaypoint waypoint[0x20]; //<! Comes from SceVfpIntRegs memblock
} SceExcpmgrExceptionContext;

typedef enum {
    // For all codes != 3, calling the last handler in the chain should call sceKernelSysrootReturnFromExcpToThreadForKernel and return properly
    SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_0 = (int16_t)0,
    SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_1 = (int16_t)1,
    SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_2 = (int16_t)2,
    // If this code reaches the last handler in the chain, calls SceDebugForKernel_082B8D6A then loops infinitely
    SCE_EXCPMGR_EXCEPTION_NOT_HANDLED_FATAL = (int16_t)3,
    SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_4 = (int16_t)4
} SceExcpmgrExceptionHandlingCode;

typedef enum {
    SCE_EXCPMGR_EXCEPTION_CODE_RESET = 0, // Reset
    SCE_EXCPMGR_EXCEPTION_CODE_UNDEF = 1, // Undefined Instruction
    SCE_EXCPMGR_EXCEPTION_CODE_SVC = 2, // Supervisor Call
    SCE_EXCPMGR_EXCEPTION_CODE_PABT = 3, // Prefetch Abort
    SCE_EXCPMGR_EXCEPTION_CODE_DABT = 4, // Data Abort
    SCE_EXCPMGR_EXCEPTION_CODE_RESERVED = 5, // Reserved
    SCE_EXCPMGR_EXCEPTION_CODE_IRQ = 6, // Interrupt
    SCE_EXCPMGR_EXCEPTION_CODE_FIQ = 7, // Fast Interrupt
} SceExcpmgrExceptionCode;

typedef struct SceExcpmgrRegistersSave { // Size is at least 0x194 on FW 0.931
    SceUInt32 FPEXC; // -0x190
    SceUInt32 FPSCR; // -0x18C
    SceUInt32 unk_0x188; // -0x188
    SceUInt32 unk_0x88[64]; // -0x88
    SceUInt32 unk_0x84; // -0x84
    SceUInt32 TPIDRURO; // -0x80
    SceUInt32 TPIDRPRW; // -0x7C
    SceUInt32 unk_0x78; // -0x78
    SceUInt32 unk_0x74; // -0x74
    SceUInt32 context_id; // -0x70
    SceUInt32 unk_0x6C; // -0x6C
    SceUInt32 unk_0x68; // -0x68
    SceUInt32 unk_0x64; // -0x64
    SceUInt32 unk_0x60; // -0x60
    SceUInt32 unk_0x5C; // -0x5C
    SceUInt32 unk_0x58; // -0x58
    SceUInt32 unk_0x54; // -0x54
    SceUInt32 unk_0x50; // -0x50
    SceUInt32 unk_0x4C; // -0x4C
    SceUInt32 unk_0x48; // -0x48
    SceUInt32 r0; // -0x44
    SceUInt32 r1;
    SceUInt32 r2;
    SceUInt32 r3;
    SceUInt32 r4;
    SceUInt32 r5;
    SceUInt32 r6;
    SceUInt32 r7;
    SceUInt32 r8;
    SceUInt32 r9;
    SceUInt32 r10;
    SceUInt32 r11;
    SceUInt32 r12;
    SceUInt32 sp;
    SceUInt32 lr;
    SceUInt32 pc;
    SceUInt32 CPSR; // -0x4
    SceUInt32 unk_0; // -0x0
} SceExcpmgrRegistersSave;

Exceptions

At coldboot startup, VBAR is set to 0x51000000. As kernel modules are loaded, SceExcpmgr's module_bootstart updates the VBAR according to the CPU core.

set_vbar(sceKernelSysrootGetVbaseResetVector() + sceKernelCpuId() * 0x40);

Note - VbaseResetVector consists of 0x120-bytes.

typedef struct _SceKernelVbaseResetVector { // size is 0x120-bytes on 3.60~3.74.
	struct {
		SceUInt32 instr[8];
		void *unk_0x20;
		void *unk_0x24; // seems thread stack
		void *unk_0x28;
		void *unk_0x2C;
		SceUInt32 padding[4];
	} core[4];
	void *handler[8];
} SceKernelVbaseResetVector;

Context

If you handle with priority 0 these contexts are not applied.

CPACR <- 0xF00000
fpexc <- 0x40000000

DBGBCR0 <- 0
DBGBCR1 <- 0
DBGBCR2 <- 0
DBGBCR3 <- 0
DBGBCR4 <- 0
DBGBCR5 <- 0

DBGWCR0 <- 0
DBGWCR1 <- 0
DBGWCR2 <- 0
DBGWCR3 <- 0

CONTEXTIDR <- 0
TTBR1 <- ?
CONTEXTIDR <- ? (could be restore)

DACR <- 0x15450000

TPIDRPRW <- 0
TPIDRURO <- 0

Registered exception handler by PSP2 Kernel

excode priority module descrption
Reset (0) 7 SceExcpmgr sceKernelDefaultHandlerReset
Lowest SceExcpmgr sceKernelDefaultHandler
Undef (1) 0 SceExcpmgr Setup the excp context.
2 SceDeci4pSDfMgr Decide whether to return to the excp point or continue with the exception after calling some callback.
4 SceKernelThreadMgr -
5 SceDeci4pDbgp Save information about the exception and continue with the exception.
6 SceKernelThreadMgr -
7 SceExcpmgr sceKernelPanicHandlerUndef
Lowest SceExcpmgr sceKernelDefaultHandler
Svc (2) 0 SceKernelIntrMgr svc handler
7 SceExcpmgr sceKernelDefaultHandlerSvc
Lowest SceExcpmgr sceKernelDefaultHandler
Pabt (3) 0 SceExcpmgr Setup the excp context.
2 SceDeci4pSDfMgr Decide whether to return to the excp point or continue with the exception after calling some callback.
4 SceKernelThreadMgr -
5 SceDeci4pDbgp Save information about the exception and continue with the exception.
Also, if the crashed address is a specific instruction, some callbacks will be called.
6 SceKernelThreadMgr -
7 SceExcpmgr sceKernelDefaultHandlerPabt
Lowest SceExcpmgr sceKernelDefaultHandler
Dabt (4) 0 SceExcpmgr Setup the excp context.
2 SceDeci4pSDfMgr Decide whether to return to the excp point or continue with the exception after calling some callback.
4 SceKernelThreadMgr -
5 SceDeci4pDbgp Save information about the exception and continue with the exception.
6 SceKernelThreadMgr -
7 SceExcpmgr sceKernelDefaultHandlerDabt
Lowest SceExcpmgr sceKernelDefaultHandler
Reserved (5) 7 SceExcpmgr sceKernelDefaultHandlerReserved
Lowest SceExcpmgr sceKernelDefaultHandler
Irq (6) 0 SceKernelIntrMgr intr handler
7 SceExcpmgr sceKernelDefaultHandlerIrq
Lowest SceExcpmgr sceKernelDefaultHandler
Fiq (7) Lowest SceExcpmgr sceKernelDefaultHandler

SVC

SVC (Supervisor Call), more commonly called Syscalls (system calls), is what allows to interact with non-secure Kernel from usermode.

The SVC interface is defined in non-secure Kernel as:

Register Value
R0 First argument
R1 Second argument
R2 Third argument
R3 Fourth argument
R12 Syscall number

On return, R1-R3 and R12 are cleared to 0x0 or 0xDEADBEEF to prevent any data leaks. All usermode pointers passed to syscalls are accessed with ARM instructions LDRT and STRT for hardware forced permission checks. Syscalls 0x0 - 0xFF are likely a "fastcall" interface that do not mask interrupts or set the DACR, however currently are no such fastcalls defined. Syscalls 0x100 - 0xFFF are made with IRQ interrupts masked and DACR set to 0xFFFF0000 (to prevent access to certain memory domains). Any other syscall numbers are invalid.

System calls are handled in "system" mode defined in ARMv7 (mode 0b11111).

Usermode exported functions loaded by SceKernelModulemgr are exported as syscalls. The number assigned to the syscall are randomized with respect to each library but not within a library. That means, for example, two functions exported by a library will always be some syscall number apart even though that number will change on each boot.

There is no SVC in secure world because all code in secure world is running as kernel.

SMC

SMC (Secure Monitor Call) is what allows to interact with ARM TrustZone from non-secure kernel.

The SMC interface for making a non-secure kernel call to TrustZone is:

Register Value
R0 First argument
R1 Second argument
R2 Third argument
R3 Fourth argument
R12 SMC number

The SMC interface is very similar to the SVC interface. The SMC handler and MVBAR is set up in TrustZone by SceExcpmgrForTZS.

0x0 - 0xFF are fast service calls. 0x100 - 0xFFF are normal service calls ran with IRQs masked.

Secure services are ran in ARM system processor mode (0b11111) in TrustZone.

SMC calls are registered by SceIntrmgrForTZS functions.

SMC calls

Number Module Arguments Notes
0x101
int AllocSharedMemory_S(void *pPA, SceSize size);
SCE_MONCALL_SET_SHARED_MEMORY - Sets the physical address of the shared memory area for Secure kernel
0x103
int FreeSharedMemory_S(void);
SCE_MONCALL_UNMAP_KBL - Frees the allocation of virtual ranges 0x40000000-0x40100000, 0x50000000-0x51000000 and 0x51000000-0x52000000 with UID 0x10007 in Secure state kernel. This does not affect Non-Secure state kernel. Called by SysStateMgr when "Unmapping Secure KBL".
0x104
int PervasiveAccessMode(int mode, SceBool cmd);
cmd: 0 clear, 1 set
0x105
int TASAccessMode(int mode, SceBool cmd);
cmd: 0 clear, 1 set
0x106
int Pervasive2AccessMode(int mode, SceBool cmd);
cmd: 0 clear, 1 set
0x107
int RegbusAccessMode(int mode, SceBool cmd);
cmd: 0 clear, 1 set
0x10A
int GetGrabCmpMap(int index);
Gets GRAB Compatibility Map.
0x10B
int SetGrabCmpMap(int index, int map);
Sets GRAB Compatibility Map.
0x10C SceKernelBusError
int BusErrorDump(void);
Used by SceKernelBusError when a bus error is handled, just before getting bus error info. Writes bus registers to (shared memory + 0x4000).
0x10D SceKernelBusError
int BusErrorDump2(void);
Used by SceKernelBusError when a bus error is handled, just before getting bus error info. Writes bus registers to (shared memory + 0x6000). Replacement of SMC 0x10C that writes to offset 0x6000 instead of 0x4000 and calls sceKernelDcacheCleanInvalidateRange before returning.
0x10E SceKernelBusError
int BusErrorClear(void);
On FW 0.931, prints a tree of all bus states. On FW 3.60, does nothing and return 0.
0x10F
int smc_ple_flush_kill_and_l1_dcache_clean_invalidate_all(void);
Flushes (and kills if there is activity) the PLE (Preload Engine) and then cleans and invalidates all the L1 Dcache.
0x110 SceDriverTzs
int smc_bus_set_state(int bus, int command);
bus: 1 -> used by SceEnumWakeUp, 2 -> used to enable/disable some things used by the PSP Emulator; maybe CPU, or memory for example. Command: 0 (stop), 1 (start), 2 (LCD DMAC init).
0x111 SceDriverTzs Related to bus.
0x113 SceDriverTzs
int smc_bus_clk_gate_switch(ScePVoid *pPervasiveGateBase, SceBool enable);
0x114 SceDriverTzs
int smc_bus_freq_op(SceUInt32 op, SceUInt32 freq_level);
op: 0 - Set bus frequency, 1 - Get bus frequency, 2 - Set GPU Xbar frequency, 3 - Get GPU Xbar frequency
0x117 SceDriverTzs
int smc_cdram_enable(void);
Seems to enable the CDRAM (by using the SceEmcTop registers).
0x118 SceDriverTzs
int smc_cdram_enter_self_resresh(void);
Enter CDRAM self-refresh mode. The resresh typo is in SCE name.
0x119 SceDriverTzs
int smc_cdram_exit_self_resresh(void);
Exit CDRAM self-refresh mode. The resresh typo is in SCE name.
0x11A SceDriverTzs
int sceSysconSetPowerMode(int type, int mode);
Set power mode. See sceSysconSetPowerModeForDriver.
0x11B SceDriverTzs
int smc_0x11B(int mode, SceBool cmd);
mode: must be < 0x1000, cmd: 0 set, 1 clear
0x11C SceDriverTzs
int smc_0x11C(SceUInt32 handle, SceInt32 value);
Used by SceLowio#SceGrabForDriver_188BBCC8.
0x11D SceDriverTzs
int sceGrabSetCmpMap(int bank, void *pPA);
Valid banks: 0-7, and physical addresses: 0x20000000-0x2FFFFFFF and 0x42800000-0x7FFFFFFF.
0x11E SceDriverTzs
int smc_compat_set_state_ex(int command, int ex);
Replacement for SMC 0x110. Used to enable/disable some things used by the PSP Emulator. Maybe the CPU, or memory for example. Command: 0 (stop), 1 (start), 2 (LCD DMAC init). Ex is only used for start (maybe for resume/suspend).
0x11F SceDriverTzs
int smc_0x11F(int unk);
Valid unk: 1-3. Waits something like a semaphore.
0x120 SceDriverTzs
int smc_compat_get_memory_bank_start_paddr(int bank);
Valid banks 0-7.
0x121 SceDriverTzs
int smc_0x121(int bank1, int bank2);
Valid banks 0-7.
0x122 SceDriverTzs
int sceCrashDumpMonitorCall(int type, void *pTTBR0, void *pVBAR, void *pSysroot);
type: 0 (exception), 1 (panic_or_assertion_failed), 2 (bus error)
0x12D
int sceSblSmSchedInvokeMonitorCall(SceBool priority, void *sm_self_paddr, SceUInt32 num_pa_range, SceSblTzsBridgeArgArea area);
Used by sceSblSmSchedProxyInvokeForKernel.
0x12E
int sceSblSmSchedWait(SceSmSchedRequestId req_id, SceSblTzsBridgeArgArea area);
Used by sceSblSmSchedProxyWaitForKernel.
0x12F
int sceSblSmSchedGetStatus(SceSmSchedRequestId req_id, SceSblTzsBridgeArgArea area);
Used by sceSblSmSchedProxyGetStatusForKernel.
0x130
int sceSblSmSchedKill(SceSmSchedRequestId req_id);
Used by sceSblSmSchedProxyKillForKernel.
0x131 Reserved (on FWs 0.931-3.60).
0x132 Reserved (on FWs 0.931-3.60).
0x133
int smc_sm_sched_proxy_call_func(SceSmSchedRequestId req_id, int mailbox_id, SceUInt32 mailval);
Used by sceSblSmSchedProxyCallFuncForKernel. mailval is paddr(SceSblSmschedCallFuncCommand) | 1.
0x134
int sceSblSmSchedReadArm2Cry(SceSmSchedRequestId req_id, int mailbox_id, SceUInt32 *pMailval);
Used by sceSblSmSchedProxyReadArm2CryForKernel.
0x135
int sceSblSmSchedWriteArm2Cry(SceSmSchedRequestId req_id, int mailbox_id, SceUInt32 mailval);
Used by sceSblSmSchedProxyWriteArm2CryForKernel.
0x136
int sceSblSmSchedWriteCry2Arm(SceSmSchedRequestId req_id, int mailbox_id, SceUInt32 mailval);
Used by sceSblSmSchedProxyWriteCry2ArmForKernel.
0x137
int sceSblSmSchedReadCry2Arm(SceSmSchedRequestId req_id, int mailbox_id, SceUInt32 *pMailval);
Used by sceSblSmSchedProxyReadCry2ArmForKernel.
0x138
int sceSblSmSchedRegisterIntrHandler(SceSmSchedRequestId req_id, int mailbox_id);
Used by sceSblSmSchedProxyRegisterIntrHandlerForKernel.
0x139
int sceSblSmSchedReleaseIntrHandler(SceSmSchedRequestId req_id, int mailbox_id);
Used by sceSblSmSchedProxyReleaseIntrHandlerForKernel.
0x13A
int smc_sm_sched_proxy_enable_all_cry2arm_interrupts(SceSmSchedRequestId req_id, int mailbox_id, SceSblTzsBridgeArgArea area);
Interrupt Cry2Arm set enable.
0x13B
int smc_sm_sched_proxy_uninitialize(void);
Interrupt Cry2Arm clear enable. Used by sceSblSmSchedProxyUninitializeForKernel.
0x13C
int smc_sm_sched_proxy_execute_sk_command(sk_cmd_index cmd_index);
Executes a Secure Kernel command. Used by sceSblSmSchedProxyExecuteSKCommandForKernel.
0x168
int smc_0x168_dmac(int index, int value);
SceDmacmgrDmac related. Writes value to SceDmacmgrDmacN + 0x100. Maybe key_id related.
0x169
int smc_0x169_dmac(int index, int value);
SceDmacmgrDmac related. Writes value to SceDmacmgrDmacN + 0x104. Maybe key_id related.
0x16A
int smc_l2_write_control_register(int value);
Flushes and invalidates the entire L2 Cache (Clean and Invalidate by Way with way = 0xFFFF, offset 0x7FC), then performs an L2 Cache Sync (offset 0x730), and finally writes value to the L2 Control Register (offset 0x100). Maybe related to PL310 Erratum 588369 (Clean and invalidate maintenance operations do not invalidate clean lines).
0x16B
int smc_l2_write_auxiliary_control_register(int value);
Writes value to the L2 Auxiliary Control Register (offset 0x104).

Aborts

On development units, data and prefetch aborts can handle BKPT instruction for software breakpoints. SceDebug uses this to handle usermode breakpoints. There is no built-in support for BKPT in kernel code.

SceSysmem uses data aborts with the LDRT and STRT instructions to implement usermode pointer checking. When LDRT/STRT throws a MMU data exception because of an invalid access and the exception came from SceSysmem#sceKernelCopyFromUserForDriver or SceSysmem#sceKernelCopyToUserForDriver or related functions, the data abort handler will resume execution.

IRQ

IRQs are only handled in non-secure world. An IRQ in secure world is fatal. See SceKernelIntrMgr.

FIQ

FIQs are only handled in secure world because of the bit set in the SCR. Because of this, it is likely that secure devices such as Cmep use FIQs to communicate with the Cortex A9 cores. See SceKernelIntrMgr.

Built-in handlers behaviour

SceExcpmgr is mostly responsible for providing a facility that allows installing exception handlers to other modules. However, it also installs exception handlers by itself.

SceExcpmgr registers an infinite loop as the default exception handler in module_start. For RESET, SVC, RESERVED and IRQ, another infinite loop handler will be installed at priority 7.

SceExcpmgr also installs a priority 7 handler for PABT, DABT and UNDEF. As specified in the Types section, those handlers will panic the kernel if they receive either SCE_EXCPMGR_EXCEPTION_NOT_HANDLED_FATAL or SCE_EXCPMGR_EXCEPTION_HANDLING_CODE_4 as their handling code parameter, infinite loop if nested exception count is greater than 1, and call sceKernelSysrootReturnFromExcpToThreadForKernel otherwise.

In addition to this, SceExcpmgr installs a priority 0 handler for PABT, DABT and UNDEF. Those three have a common behaviour: building the SceExcpmgrExceptionContext, incrementing the nested exception count, clearing some CP14 control registers if needed, loading the SceProcessContext provided by SceSysrootForKernel_118657C6, setting DACR to 0x15450000 (not done in System Software version 0.990.030), zero'ing out TPIDRPRW and TPIDRURO, and calling a per-handler callback. The CP14 registers that get zero'ed out are the breakpoint control registers (DBGBCR0-DBGBCR5) if a non-NULL pointer has been provided to SceExcpmgrForKernel_A66DDFA3, and the watchpoint control registers (DBGWCR0-DBGWCR3) if a non-NULL pointer has been provided to SceExcpmgrForKernel_4FF90618.

PABT handler

The PABT handler performs no additional work and merely transfers control to the next exception handler in chain.

UNDEF handler

In System Software version 0.990.030, the UNDEF handler performs no additional work and merely transfers control to the next exception handler in chain.

Since System Software version ?.??, the UNDEF handler will check if the instruction that caused the exception is a MCRR p15, 0, Rt, Rt2, c11, in which case it will be replaced with a NOP before execution is resumed at the faulting instruction (which is now a NOP). Otherwise, control is transferred to the next exception handler in chain. According to the ARM Cortex-A9 TRM, this MCRR instruction is used to program the Preload Engine channels.

DABT handler

The DABT handler is responsible for handling aborts in the Vmaccess routines. If such an exception has not happened, control is transferred to the next exception handler in chain.

Old Vmaccess model

In System Software revision 0.931.010 and earlier, the Vmaccess functions follow this pattern:

CopyFromUser32:
    ldrt r2, [r0] @or any load from kernel/user
    str r2, [r1]  @or any store to kernel/user
    mov r0, 0     @Return value = SCE_OK
    cpy r0, r0
    cpy r0, r0    @nop
    bx lr

If an invalid (user) pointer was provided to this function, a Data Abort would occur on the checked instruction (in this case, ldrt).

When an exception occurs, if the CPU is in ARM mode and pc belongs in the old model Vmaccess range, the DABT handler will set r0 = 0x80022005 (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS) in the exception context then resume execution 'three instructions after the faulting one', in the nopsled (in modern firmwares, only one instruction is skipped instead of 3). The caller thus see the function failed as it returned an error code (SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS).

In recent firmwares, the old Vmaccess range must be explicitely set via a call to SceExcpmgrForKernel_C45C0D3D instead of being fetched during the call to sceKernelGetVmaccessRange.

New Vmaccess model

Since System Software revision 0.940, this model has been progressively phased out and replaced with a new Vmaccess model.

Under the new model, the Vmaccess functions all begin by loading the address of a function in r12 but no longer have to follow a specific pattern. When an exception occurs, if the CPU is in ARM mode and the instruction at pc is a checked memory access instruction (i.e. one of {strt, strht, strbt, ldrt, ldrht, ldrbt, ldrsht, strsbt}), execution is resumed at the address pointed to by r12. The address loaded in r12 points to a snippet that cleans the stack if needed then returns SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS.

Note that if the old and new model ranges overlap, the latter takes priority. This means that exceptions raised from code residing in the overlap will be handled under the new model.

If the DABT or UNDEF handlers return, CP14 registers are restored from either the global variables set previously by SceExcpmgrForKernel_4FF90618 and SceExcpmgrForKernel_A66DDFA3, or interrupted thread's TPIDRPRW->0x18->0xC if the TPIDRPRW is non-NULL. If no breakpoint restore structure has been provided to SceExcpmgrForKernel_A66DDFA3, then watchpoints will not be restored regardless of whether something was provided to SceExcpmgrForKernel_4FF90618 or not.

Non-exported functions

The following table contains offsets (from the base of .text/segment 0) to non-exported functions whose name is known:

Function name 0.990.030 3.600.011 CEX
sceKernelDefaultHandlerCfunc 0x2138 0x1D4C
sceKernelDefaultHandlerResetCfunc 0x21A8 0x1D50
sceKernelDefaultHandlerSvcCfunc 0x21D0 0x1D54
sceKernelDefaultHandlerReservedCfunc 0x2238 0x1D58
sceKernelDefaultHandlerIrqCfunc 0x2260 0x1D5C
sceKernelPanicHandlerUndefCfunc 0x2288 0x1D60
sceKernelPanicHandlerPabtCfunc 0x22C4 0x1D78
sceKernelPanicHandlerDabtCfunc 0x2304 0x1D90

SceExcpmgrForKernel

sceKernelRegisterExceptionHandlerForKernel

Version NID
0.990-3.60 0x03499636
3.65 0x00063675

Temp name was sceExcpmgrRegisterHandlerForKernel.

Installs an exception handler. The exception handler can be ARM code or Thumb code. Allowed priority values are from 0 (most important) to 7 (least important), including them. Specifying priority 0 will install the handler directly in VBAR, which means its code will run directly when an exception raises (and as such it needs to build the exception context itself). handler & 0x1 must be set if the handler function is Thumb code.

The syscall handler in FW 1.50 is SceExcpmgr_func_0x81000E40.

The syscall handler in FW 3.65 is located at offset 0xF40 in SceKernelIntrMgr's .text segment.

int sceKernelRegisterExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);

sceKernelRegisterDefaultExceptionHandlerForKernel

Version NID
0.931-3.60 0xFFCCB5F9
3.65 0x416C0E20

Installs an exception handler for all exceptions. If no exception handler is already installed for an exception, the handler will be installed in the VBAR. handler must be the same parameter that would be passed to sceKernelRegisterExceptionHandlerForKernel.

int sceKernelRegisterDefaultExceptionHandlerForKernel(SceExcpmgrExceptionHandlerContext *handler);

sceKernelReleaseExceptionHandlerForKernel

Version NID
0.990-3.60 0xE04AE286
3.65 0xDA7BB671

Uninstalls an exception handler.

Pass the same arguments as provided to sceKernelRegisterExceptionHandlerForKernel.

int sceKernelReleaseExceptionHandlerForKernel(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);

sceKernelReleaseDefaultExceptionHandlerForKernel

Version NID
0.990 0x917A6D2B
3.60 not present

sceKernelInitialHandlerDebugUndefCfuncForKernel

Version NID
0.931-3.60 0xB77DCBD6
3.65 0x64A057C7

Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.

void sceKernelInitialHandlerDebugUndefCfuncForKernel(SceExcpmgrExceptionContext* context);

sceKernelInitialHandlerDebugPabtCfuncForKernel

Version NID
0.931-3.60 0x25C0C91B
3.65 0x64A057C7

Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.

void sceKernelInitialHandlerDebugPabtCfuncForKernel(SceExcpmgrExceptionContext* context);

sceKernelInitialHandlerDebugDabtCfuncForKernel

Version NID
0.990-3.60 0xFFFA3353
3.65 0xD195E55C

Logs some information about the CPU state and backtrace then returns. Freezes with an infinite loop if currentHandlerIndex is superior to 1.

void sceKernelInitialHandlerDebugDabtCfuncForKernel(SceExcpmgrExceptionContext* context);

sceKernelReturnFromExceptionForKernel

Version NID
0.931-3.60 0xD17EEE40
3.65 0x3E55B5C3

This is a guessed name.

void sceKernelReturnFromExceptionForKernel(SceExcpmgrExceptionContext *context);

SceExcpmgrForKernel_A90AC525

Version NID
0.990-3.60 0xA90AC525
3.65 0x4337DD78

Returns a pointer to the handler previously registered by sceKernelRegisterDefaultExceptionHandlerForKernel.

SceExcpmgrExceptionHandlerContext *SceExcpmgrForKernel_A90AC525(void);

sceKernelGetExcpStackBottomForKernel

Version NID
0.990-3.60 0x4C603645
3.65 0x5420ED8F

Returns the exception stack bottom for specified CPU core. The stack bottom is the highest address of the stack.

Reimplementation code:

void* sceKernelGetExcpStackBottomForKernel(int coreNum) {
    return sceKernelGetExcpDataForKernel()->ExcpStackBottom[coreNum];
}
void* sceKernelGetExcpStackBottomForKernel(int coreNum);

sceKernelGetExcpCBForKernel

Version NID
0.931-0.990 not present
3.60 0x08CB30E6
3.65 0x96C2869C

This is a guessed name. Temp name was sceKernelGetExcpDataForKernel, sceExcpmgrGetDataForKernel.

Get exception data pointer (for use by handlers).

SceExcpmgrCB *sceKernelGetExcpCBForKernel(void);

SceExcpmgrForKernel_3AE9AEE1

Version NID
0.931-0.990 not present
3.60 0x3AE9AEE1
3.65 0x1FBF5654

Sets a callback function ran by the default UNDEF exception handler before calling the next handler in the chain.

// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_3AE9AEE1(void (*callback)(SceExcpmgrExceptionContext*));

SceExcpmgrForKernel_4FF90618

Version NID
0.990-3.60 0x4FF90618
3.65 0xE7487AFD

Called by SceProcessmgr during CreateKernelProcess.

Sets the structure CP14 watchpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See SceExcpmgr#Provided handlers' behaviour for more information.

void SceExcpmgrForKernel_4FF90618(DBGWVC_save *a1);

SceExcpmgrForKernel_A66DDFA3

Version NID
0.990-3.60 0xA66DDFA3
3.65 0x293DFA04

Called by SceProcessmgr during CreateKernelProcess.

Sets the structure CP14 breakpoint registers will be restored from if TPIDRPRW of thread that raised an exception is NULL (Kernel process). See SceExcpmgr#Provided handlers' behaviour for more information.

void SceExcpmgrForKernel_A66DDFA3(DBGBVC_save *a1);

SceExcpmgrForKernel_7ADF11DB

Version NID
0.931-0.990 Not present
3.60 0x7ADF11DB
3.65 0x9EE59C6E

Sets a callback function ran by the default DABT exception handler before trying to handle the exception.

// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_7ADF11DB(void (*callback)(SceExcpmgrExceptionContext*));

SceExcpmgrForKernel_8D223205

Version NID
0.931-0.990 not present
3.60 0x8D223205
3.65 0x58F7212B

Sets a callback function ran by the default PABT exception handler before calling the next handler in the chain.

// NOTE : context passed to callback lacks VFP registers
void SceExcpmgrForKernel_8D223205(void (*callback)(SceExcpmgrExceptionContext*));

SceExcpmgrForKernel_C45C0D3D

Version NID
0.931-0.990 Not present
3.60 0xC45C0D3D
3.65 0x44CE04B8

Sets the "memory access error area" range. See the built-in handlers behaviour section for more information.

In older firmwares (0.990.030), this range is acquired during the call to SceCpuForKernel_9A3281C0 in SceExcpmgr module_start instead.

void SceExcpmgrForKernel_C45C0D3D(SceUIntPtr memErrorAreaStart, SceUIntPtr memErrorAreaEnd);

SceExcpmgrForKernel_D464A9A7

Version NID
0.931-3.60 0xD464A9A7
3.65 0xB615A7DA

Returns the current nested exception count, or 0 if current TPIDRPRW is non-NULL.

int SceExcpmgrForKernel_D464A9A7(void);

SceExcpmgrForTZS

SceExcpmgrForTZS_get_default_excp_handler

Version NID
0.931-1.80 0x07A5790B
void *SceExcpmgrForTZS_get_default_excp_handler(void);

sceKernelReleaseExceptionHandlerForTZS

Version NID
0.931-1.80 0x166C9362
int sceKernelReleaseExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler)

sceKernelReleaseDefaultExceptionHandlerForTZS

Version NID
0.931-1.80 0x6282E52C
int sceKernelReleaseDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)

sceKernelRegisterDefaultExceptionHandlerForTZS

Version NID
0.931-1.80 0xA0434735
int sceKernelRegisterDefaultExceptionHandlerForTZS(SceExcpmgrExceptionHandlerContext *handler)

sceKernelRegisterMonitorEntryForTZS

Version NID
0.931-3.60 0xAC297406
int sceKernelRegisterMonitorEntryForTZS(SceExcpmgrExceptionCode excode, SceExcpmgrExceptionHandlerContext *handler);

sceKernelRegisterExceptionHandlerForTZS

Version NID
0.931-1.80 0xDD4C680D
int sceKernelRegisterExceptionHandlerForTZS(SceExcpmgrExceptionCode excode, SceUInt32 priority, SceExcpmgrExceptionHandlerContext *handler);