SceExcpmgr: Difference between revisions
CelesteBlue (talk | contribs) No edit summary |
|||
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 | 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. | ||
Line 39: | Line 39: | ||
void *ExcpStackTop[4]; // Stack size is 0x1000 | void *ExcpStackTop[4]; // Stack size is 0x1000 | ||
void *ExcpStackBottom[4]; | void *ExcpStackBottom[4]; | ||
} | } SceExcpmgrCB; | ||
// Exception handlers' prototype | // Exception handlers' prototype | ||
Line 414: | Line 414: | ||
Get exception data pointer (for use by handlers). | Get exception data pointer (for use by handlers). | ||
<source lang="C"> | <source lang="C">SceExcpmgrCB *sceKernelGetExcpCBForKernel(void);</source> | ||
=== SceExcpmgrForKernel_3AE9AEE1 === | === SceExcpmgrForKernel_3AE9AEE1 === |
Revision as of 19:04, 9 September 2022
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-3.65 | Non-secure | Kernel |
0.931-1.69 | Secure | Kernel |
Libraries
This module only exports kernel libraries.
Known NIDs
Version | Name | World | Visibility | NID |
---|---|---|---|---|
0.931-3.61 | SceExcpmgrForKernel | Non-secure | Kernel | 0x4CA0FDD5 |
3.63-3.65 | SceExcpmgrForKernel | Non-secure | Kernel | 0x1496A5B5 |
0.931-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 _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]; } 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; // 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 unused_0x68; // Seemingly not populated 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 unused_0xD0; SceUInt32 unk_0xD4; // Comes from SceVfpIntRegs[localCore].field_0x100 SceUInt32 DBGSCRext; SceUInt32 unused_0xDC[9]; // Seemingly not populated SceUInt64 VFP_registers[32]; // Content of registers d0-d31 SceUInt32 unk_0x200[128]; // Comes from SceVfpIntRegs[localCore].field_0x200 } 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;
Provided handlers' behaviour
SceExcpmgr is mostly responsible for providing a facility that allows installing exception handlers to other modules. However, it also installs some exception handlers from 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 0.990), 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.
The PABT handler will then proceed to call the next handler in chain. (In 0.990, the UNDEF handler also does this)
The UNDEF handler will check if the instruction that caused the exception to be raised is a MCRR p15, 0, Rt, Rt2, c11
, and replace it with a nop
then resume execution at the faulting instruction (which is now a nop
).
Otherwise, the next handler in chain is called.
According to the ARM Cortex-A9 TRM, this MCRR instruction is used to program the Preload Engine channels.
The DABT handler will check if the CPU was in Thumb mode (check not done in 0.990), then act according to the following logic:
- if the faulting instruction resides between
UKCopyAreaStart
(included) andUKCopyAreaEnd
(excluded) provided by SceCpuForKernel_9A3281C0, and is eitherstrt
,strht
,strbt
,ldrt
,ldrht
,ldrbt
,ldrsht
orstrsbt
), then execution is resumed at the address located inr12
. This is used by functions like SceSysmem#sceKernelCopyFromUser. - if the previous check fails, and the faulting instruction resides between
memErrorAreaStart
(included) andmemErrorAreaEnd
provided to SceExcpmgrForKernel_C45C0D3D,SCE_KERNEL_ERROR_INVALID_MEMORY_ACCESS
is copied intor0
and execution is resumed at the instruction right after the faulting one (3 instructions after the faulting one in old firmwares). This mechanism seems to be unused in 3.65. - otherwise, the next handler in chain is called.
Note that, in the situation where the ranges provided by SceCpuForKernel_9A3281C0 and SceExcpmgrForKernel_C45C0D3D overlap, the former takes priority. This means that exceptions raised from code residing in the overlap will be handled as if it only belonged in the first range.
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.60 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);
sceKernelPanicHandlerReturnFromNestedExceptionCfuncForKernel
Version | NID |
---|---|
0.931-3.60 | 0xD17EEE40 |
3.65 | 0x3E55B5C3 |
This name is probably incorrect.
void sceKernelPanicHandlerReturnFromNestedExceptionCfuncForKernel(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(void* 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(void* 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 SceExcpmgr#Provided handlers' behaviour for more information.
In older firmwares (0.990), 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);
Exceptions
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 user 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).
User 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 secure kernel 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 Secure World 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 the Secure World.
SMC calls are registered by SceIntrmgrForTZS functions.
SMC calls
Number | Module | Arguments | Notes |
---|---|---|---|
0x101 | int AllocSharedMemory_S(void *pPA, SceSize size); |
||
0x103 | int FreeSharedMemory_S(void); |
Called by SysStateMgr when "Unmapping Secure KBL" - probably unmapSKBL . Frees the allocation of virtual ranges: 0x40000000 -0x40100000 , 0x50000000 -0x51000000 and 0x51000000 -0x52000000 with UID 0x10007. This is SKBL only and does not affect NSKBL.
| |
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 | Related to bus. | |
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_0x118(void); |
Seems to enable something CDRAM related. |
0x119 | SceDriverTzs | int smc_0x119(void); |
Seems to disable something CDRAM related. |
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 smc_compat_set_memory_bank_start_paddr(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 smc_intr_crash(int type, void *pTTBR0, void *pVBAR, void *pSysroot); |
type: 0 (exception), 1 (panic_or_assertion_failed), 2 (bus error) |
0x12D | int sceSblSmSchedInvoke(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 ).
| |
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 user 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 the F00D Processor use FIQs to communicate with the Cortex A9 cores. See SceKernelIntrMgr.