NSKBL: Difference between revisions
CelesteBlue (talk | contribs) No edit summary |
CelesteBlue (talk | contribs) |
||
(16 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
Non-Secure Kernel Boot Loader (NSKBL) is | The Non-Secure Kernel Boot Loader (NSKBL) is the first program that runs on ARM cores in Non-Secure state. As its name indicate, it has the role of initializing the Non-Secure state environment and loading the kernel from storage. | ||
== Module == | == Module == | ||
NSKBL is made of stripped down versions of some Non-Secure kernel modules along with code using all these functions to prepare the non-secure environment and load the next stage. | |||
Reduced versions of the following modules are embedded in NSKBL: | |||
* [[SceSysmem]] | |||
* [[SceKernelModulemgr]] | |||
* [[SceExcpmgr]] | |||
* [[SceKernelIntrMgr]] | |||
* [[SceSblAuthMgr]] | |||
* [[SceSblSmschedProxy]] | |||
* [[SceLowio]] (only parts of the Pervasive submodule) | |||
* [[SceSdif]] | |||
** <code>iofilemgr_if</code> serves as a thin wrapper around [[SceSdif]] for compatibility | |||
*** Only <code>sceIoOpen</code>, <code>sceIoLseek</code>, <code>sceIoRead</code> and <code>sceIoClose</code> are provided | |||
*** This is just a '''wrapper'''. [[SceIofilemgr]] is '''not''' included in NSKBL! | |||
It is very likely that the embedded modules in NSKBL are built from the same source tree as the Non-Secure kernel modules. | |||
This can be inferred from debugging information contained in some modules (line numbers in NSKBL and module equivalent match). | |||
== Notes == | == Notes == | ||
Line 14: | Line 30: | ||
This error can occur if the file is fragmented. | This error can occur if the file is fragmented. | ||
== NSKBL walk through == | |||
This section aims to provide an overview of the tasks performed by NSKBL. This description is based on System Software version 3.50 and later. Older versions may behave differently. See also [[NSKBL Subroutines]]. | |||
=== Reset === | |||
The NSKBL reset vector, located at <code>0x51000000</code>, is the first instruction executed in Non-Secure state, with the MMU turned off. | |||
It jumps to a small ASM function that disables interrupts (<code>cpsid if</code>), clears exclusive access requests (<code>clrex</code>), invalidates all caches, sets up stacks for all processors modes, and calls <code>boot</code>. | |||
=== <code>boot</code> === | |||
This function is responsible for turning on the MMU. It partially initializes the <code>SceKernelBootParam</code>, copies the [[KBL Param]] from <code>0x40200100</code> (where it was placed by [[SKBL]]) and prints the <code>Starting PSP2 Kernel Boot Loader (Non-secure)</code> message. | |||
The <code>SceKernelBootParam</code> initialization includes allocating physical and virtual pages for things like page tables, TTBR, fixed heaps, Sysroot, PhyPageTable, etc. The virtual address for the smaller mappings is randomized in the first 1 MiB of the address space (255 page-sized slots - first slot is reserved for NULL page), while larger allocations are randomized in the first 16 MiB (15 section-sized slots - slot 0 is reserved for the small ASLR mappings). A few mappings are exempted from randomization, like the identity mapping of <code>0x51000000</code>-<code>0x51FFFFFF</code> where NSKBL resides. This memory range is mapped as RWX and seems to be the only RWX mapping in the Non-Secure kernel. | |||
After all these mappings are complete, the MMU is turned on along with caches, branch predictor, and a few other things via the <code>SCTLR</code>. At this point, <code>boot</code> is done and <code>sceKblMain</code> is called. | |||
=== <code>sceKblMain</code> === | |||
This function runs with the MMU enabled and receives a partially filled-in <code>SceKernelBootParam</code> from <code>boot()</code>. | |||
This function begins with a call to <code>sceKernelSysrootStart</code> which initializes the Sysroot structure. The slack space after the structure in the <code>SceSysroot</code> mapping (which is 16KiB-sized) is turned into a "heap" as part of this process. Memory allocation needs are serviced from this space using <code>sceKernelSysrootAlloc</code>. At this point, there are no other kernel heaps available. Sysroot status is set to 1 by this function. | |||
After the Sysroot is initialized, <code>sceKernelSysmemStart</code> is called, which initializes the embedded [[SceSysmem]] module. It is at this stage that all the Sysmem object classes are created. Sysroot status is set to 2 then 3 by this function. Kernel heaps are available after the Sysroot status is set to 3 i.e. when this function returns. | |||
Now that Sysmem is initialized, 16-KiB-sized <code>SceKernelBootStackCoreX</code> memory blocks are allocated, and execution is transfered to <code>main2</code> after switching the stack pointer to these memblocks. Up until now, the stack it used was located in NSKBL <code>.bss</code> section. | |||
=== <code>main2</code> === | |||
The <code>main2</code> function receives a filled-up <code>SceKernelBootParam</code> from <code>sceKblMain()</code>. | |||
After setting the Sysroot status to 4, the remaining embedded modules are started (in order: <code>sceKernelModulemgrStart()</code>, [[SceExcpmgr]] start, <code>sceKernelIntrMgrStart</code>, <code>sceSblAuthMgrStart()</code> and [[SceSblSmschedProxy]] start). Once the embedded modules are started, Sysroot status is set to 0x10 and the final NSKBL function is called. | |||
=== Final function === | |||
The final function receives the same <code>SceKernelBootParam</code> as <code>main2</code>. | |||
It first calls [[NSKBL#sceSdStandaloneInitForKernel|sceSdStandaloneInit]] then loads and caches the <code>gcauthmgr</code>, <code>rmauth</code> and <code>encdec_w_portability</code> security modules. | |||
After this, it loads [[ScePsp2BootConfig|<code>os0:kd/psp2bootconfig.skprx</code>]], links the [[NSKBL#SceKblForKernel|SceKblForKernel library]] to the module, and starts it with <code>SceKernelBootParam</code> address as <code>argp</code>. | |||
NSKBL has now handed over control and simply remains resident until it is unmapped by [[SceSysStateMgr]]. See also [[SceKernelModulemgr#sceKernelFinalizeKblForKernel]]. | |||
== Types == | == Types == | ||
Line 19: | Line 78: | ||
<source lang="C"> | <source lang="C"> | ||
typedef struct SceNskblModuleInfo { | typedef struct KBPMemoryRegion { | ||
char* | SceUIntPtr pbase; | ||
SceUID moduleId; //SCE_UID_INVALID_UID | SceSize psize; | ||
SceUInt32 loadFlags; //Passed as flags to | } KBPMemoryRegion; | ||
} SceNskblModuleInfo; | |||
typedef struct KBPMappingInfo { | |||
char *name; //<! Name of the mapping | |||
SceUIntPtr pbase; //<! Base physical address of the mapping | |||
SceUIntPtr vbase; //<! Base virtual address of the mapping | |||
SceSize psize; //<! Size of the physical range of the mapping?? | |||
SceSize vsize; //<! Size of the virtual range of the mapping?? - Usually equal to psize, but may be bigger. | |||
SceUInt32 extraHigh; //<! Sometimes used as extraHigh for some memblock allocations? Exact meaning unknown. | |||
// psize + extraHigh must always be equal to vsize | |||
} KBPMappingInfo; | |||
typedef struct KBPBootStackInfo { | |||
unsigned unk0[2]; | |||
SceUIntPtr ttbcr; | |||
SceUInt32 dacr; | |||
SceUInt32 asid; | |||
SceSize size; | |||
SceUID blkId; //<! UID of the stack's memblock | |||
void *stackBottom; //<! = memblock.vbase + size | |||
} KBPBootStackInfo; | |||
// This structure is passed as argp of modules like sysmem.skprx, etc | |||
// 0x51030100 on NSKBL | |||
typedef struct SceKernelBootParam { // Size is 0x310-bytes in 3.60-3.65 and 4.00. Layout appears to be identical. | |||
SSceSize size; //<! Size of this structure | |||
SceBool secure; //<! Is secure state? Always 0 in NSKBL | |||
SceUInt32 num_memory; //<! Number of filled elements in the next field | |||
KBPMemoryRegion memory[4]; //<! Physical memory ranges usable by NSKBL and kernel | |||
SceKblParam *pKblParam; //<! Pointer to KBL Param | |||
unsigned unk30[5]; | |||
SceUInt32 SoCInfo; //<! = *(u32*)PERVASIVE_MISC | |||
SceUInt32 pmisc_unk4; //<! = *(u32*)(PERVASIVE_MISC + 4) | |||
SceUInt32 KermitRevision; //<! = SoCInfo & 0x1FFFF | |||
SceUInt32 hwDependent[2]; //<! Depends on pKBLParam->hardwareInfo | |||
KBPMappingInfo ttbr0; //<! "SceKernelTTBR0", ASLR vbase, psize 0x4000 | |||
unsigned ttbr0_maxAddress; //<! Maximal address covered by TTBR0 | |||
unsigned sizeTTBR0Address; //<! Size of the address space covered by TTBR0 | |||
KBPMappingInfo ttbr1; //<! "SceKernelTTBR1", ASLR vbase, psize 0x4000 | |||
unsigned sizeTTBR1Address; //<! Size of the address space covered by TTBR1 | |||
void *L2PT000_mapbase; //<! First vaddr mapped by "SceKernelL2PageTable000" | |||
KPBMappingInfo reset; //<! "SceKernelReset", vbase 0, psize 0x1000, extraHigh 0x3000 | |||
KBPMappingInfo excpEntry; //<! "SceKernelExceptionEntry", ASLR vbase, psize 0x1000, extraHigh 0x1000 | |||
KBPMappingInfo l2pt000; //<! "SceKernelL2PageTable000", ASLR vbase, psize 0x1000, extraHigh 0x1000 | |||
KBPMappingInfo l2vector; //<! "SceKernelL2Vector", ASLR vbase, psize 0x4000, extraHigh 0x4000 | |||
KBPMappingInfo sysroot; //<! "SceSysroot", ASLR vbase, psize 0x4000 | |||
KBPMappingInfo fh32b; //<! "SceKernelFixedHeap32B", ASLR vbase, psize 0x10000 | |||
KBPMappingInfo fh48b; //<! "SceKernelFixedHeap48B", ASLR vbase, psize 0x10000 | |||
KBPMappingInfo fh64b; //<! "SceKernelFixedHeap64B", ASLR vbase, psize 0x10000 | |||
KBPMappingInfo fhUIDEntry; //<! "SceKernelFixedHeapUIDEntry", ASLR vbase, psize 0x10000 | |||
KBPMappingInfo fhL2Object; //<! "SceKernelFixedHeapForL2Object", ASLR vbase, psize 0x1000, extraHigh 0x1000 | |||
KBPMappingInfo unk188; //<! Unused? NOTE: ASLR vbase = randomize in 255 slots, MegaASLR vbase = randomize in 15 slots | |||
KBPMappingInfo phypage; //<! "SceKernelPhyPageTable", MegaASLR vbase, psize 0x80000 | |||
KBPMappingInfo phypageHigh; //<! "SceKernelPhyPageTableHigh", MegaASLR vbase, psize 0x80000, pbase 0x77F00000 | |||
KBPMappingInfo bootkernimg; //<! "SceBootKernelImage", vbase=pbase=0x51000000, psize 0x1000000, extraHigh 0x1000000 | |||
KBPMappingInfo hwreg; //<! Unnamed. vbase=pbase=0xE0000000, psize 0x8000000 | |||
SceCorelockContext *pCorelock; | |||
KBPBootStackInfo bootCpu[4]; //<! bootCpu[X] corresponds to CPUX's info. Stack is pivoted to this when calling main2. | |||
SceSysroot *pSysroot; //<! Pointer to Sysroot structure | |||
unsigned unk288; //<! Related to L2PageTable000? Always 0. | |||
void *pL2PageTable000; //<! Base address of the L2PageTable000 | |||
void *resetVector; //<! Goes into VBAR. = excpEntry.vbase + 0x100 | |||
SceKernelPhyMemPart *phyMemPartKD; //<! "SceKernelPhyMemPartKD" | |||
SceKernelPhyMemPart *phyMemPartTool; //<! "SceKernelPhyMemPartTool". May be NULL | |||
PhyPage *pPageKernelReset; //<! PhyPage object describing the pages backing SceKernelReset | |||
PhyPage *pPageL2PageTable000; //<! PhyPage object describing the pages backing SceKernelL2PageTable000 | |||
PhyPage *pPageSysroot; //<! PhyPage object describing the pages backing SceSysroot | |||
PhyPage *pPageTTBR0; //<! PhyPage object describing the pages backing SceKernelTTBR0 | |||
PhyPage *pPageTTBR1; //<! PhyPage object describing the pages backing SceKernelTTBR1 | |||
PhyPage *pPageL2Vector; //<! PhyPage object describing the pages backing SceKernelL2Vector | |||
PhyPage *pPagePhypage; //<! PhyPage object describing the pages backing SceKernelPhyPageTable | |||
PhyPage *pPagePhypageHigh; //<! PhyPage object describing the pages backing SceKernelPhyPageTableHigh | |||
PhyPage *pPageBootKernelImage; //<! PhyPage object describing the pages backing SceBootKernelImage???? | |||
PhyPage *pPageFixedHeap32B; //<! PhyPage object describing the pages backing SceKernelFixedHeap32B | |||
PhyPage *pPageFixedHeap48B; //<! PhyPage object describing the pages backing SceKernelFixedHeap48B | |||
PhyPage *pPageFixedHeap64B; //<! PhyPage object describing the pages backing SceKernelFixedHeap64B | |||
PhyPage *pPageFixedHeapForL2Object; //<! PhyPage object describing the pages backing SceKernelFixedHeapForL2Object | |||
SceUIDFixedHeapObject *pFixedHeap32B; | |||
SceUIDFixedHeapObject *pFixedHeap48B; | |||
SceUIDFixedHeapObject *pFixedHeap64B; | |||
SceUIDFixedHeapObject *pFixedHeapForL2Object; | |||
PhyPage *pPageUIDHeap; //<! PhyPage object describing the pages backing SceKernelFixedUIDHeap?? | |||
SceUIDEntryHeapObject *pUIDHeap; //<! Pointer to SceKernelFixedUIDHeap object | |||
L2PageTableObject *pL2PT000Object; //<! L2PageTableObject for SceKernelL2PageTable000 | |||
L2PageTableObject *pPhyPageTblL2PTO; //<! L2PageTableObject for table used to map the PhyPageTable (located @ SceKernelL2PageTable000.pbase + 0x400) | |||
SceUIDPartitionObject *pPartitionKernel; //<! Object of "SceKernelRoot" partition | |||
SceUID uidPartitionKernel; //<! UID of object above (0x10009) | |||
SceUInt32 unk2F8[2]; | |||
void *data_0x300; | |||
void *pPutcharHandler; | |||
SceUInt32 minimum_log_level; | |||
SceUInt32 magic; //<! 0x7F407C30 | |||
} SceKernelBootParam; | |||
typedef struct SceNskblModuleInfo { // size is 0xC on FWs 0.940-0.990 | |||
char* filename; // Raw SKPRX file name (e.g. "sysmem.skprx"). Modules are loaded either from os0:kd/ or host0:module/. | |||
SceUID moduleId; // SCE_UID_INVALID_UID. It gets filled when loading. | |||
SceUInt32 loadFlags; // Passed as flags to sceKernelLoadModule. | |||
} __attribute__((packed)) SceNskblModuleInfo; | |||
typedef struct SceNskblModuleInfo2 { // size is 4 on FW 3.60 | |||
const char* filename; | |||
} __attribute__((packed)) SceNskblModuleInfo2; | |||
typedef struct SceHardwareFlags { // size is 0x10 on FW 3.60 | |||
uint32_t data[4]; | |||
} __attribute__((packed)) SceHardwareFlags; | |||
/* Many pointers are NSKBL heap relationships */ | /* Many pointers are NSKBL heap relationships */ | ||
Line 95: | Line 259: | ||
== SceKblForKernel == | == SceKblForKernel == | ||
NOTE: this library is linked directly to <code>psp2bootconfig</code> instead of being registered, and can thus only be imported by this module. | |||
=== sceSDrfpStartForKernel === | === sceSDrfpStartForKernel === | ||
Line 119: | Line 285: | ||
<source lang="C">SceInt32 sceSDbgSdioStartForKernel(void);</source> | <source lang="C">SceInt32 sceSDbgSdioStartForKernel(void);</source> | ||
=== sceSDfMgrStartForKernel === | === sceSDfMgrStartForKernel === | ||
Line 159: | Line 295: | ||
| 3.60 || not present | | 3.60 || not present | ||
|} | |} | ||
=== sceKblPutcharForKernel === | === sceKblPutcharForKernel === | ||
Line 224: | Line 344: | ||
|} | |} | ||
Temp name was | Temp name was sceKblGetMinimumLogLevelForKernel. | ||
In FW 3.60 this function is at 0x51013921. | In FW 3.60 this function is at 0x51013921. | ||
Line 302: | Line 422: | ||
<source lang="C">void sceKernelSysrootCorelockLockForKernel(SceUInt32 core);</source> | <source lang="C">void sceKernelSysrootCorelockLockForKernel(SceUInt32 core);</source> | ||
=== | === sceKblCpuSuspendIntrForKernel_old === | ||
{| class="wikitable" | |||
|- | |||
! Version !! NID | |||
|- | |||
| 0.940-0.990 || 0x99B2F981 | |||
|- | |||
| 3.60 || not present | |||
|} | |||
This is a guessed name. | |||
On FW 0.940, it calls a routine that simply executes <code>cpsid i</code> then returns 0. | |||
CPSID i ; Disable all interrupts except NMI (set PRIMASK) | |||
Disables IRQ interrupts by setting the I-bit in the CPSR. | |||
=== sceKblCpuSuspendIntrForKernel_new === | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 312: | Line 450: | ||
|} | |} | ||
This is a guessed name. Temp name was sceKblCpuSwitchInterruptsForKernel. | This is a guessed name. Temp name was sceKblCpuSwitchInterruptsForKernel, sceKblCpuDisableIrqInterruptsForKernel. | ||
In FW 3.60 this function is at 0x51003554. | In FW 3.60 this function is at 0x51003554. | ||
<source lang="C">void | <source lang="C">void sceKblCpuSuspendIntrForKernel_new(void);</source> | ||
=== sceSblAimgrIsCEXForKernel === | === sceSblAimgrIsCEXForKernel === | ||
Line 426: | Line 564: | ||
<source lang="C">int sceSblAimgrIsGenuineDolceForKernel(void);</source> | <source lang="C">int sceSblAimgrIsGenuineDolceForKernel(void);</source> | ||
=== | === LoadModulesForKernel === | ||
{| class="wikitable" | |||
|- | |||
! Version !! NID | |||
|- | |||
| 0.940-0.990 || 0xFAE33FDD | |||
|- | |||
| 3.60 || not present | |||
|} | |||
Load all modules from the provided list. The list end is marked by an entry with <code>moduleName = NULL</code>. | |||
Module GUIDs are populated into the list, so it must be writeable. | |||
<source lang="C">SceInt32 LoadModules(SceNskblModuleInfo* module_list);</source> | |||
=== sceKernelBootLoadModulesForKernel === | |||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 436: | Line 590: | ||
|} | |} | ||
Temp name was sceKblLoadModulesForKernel. | |||
In FW 3.60 this function is at 0x51001551. | In FW 3.60 this function is at 0x51001551. | ||
<source lang="C">int sceKernelBootLoadModulesForKernel(const SceNskblModuleInfo2 *pList, SceUID *pUidList, SceUInt32 count, SceBool use_tool_extended_memory);</source> | |||
=== BootModulesForKernel === | |||
{| class="wikitable" | |||
|- | |||
! Version !! NID | |||
|- | |||
| 0.940-0.990 || 0xA7D60F71 | |||
|- | |||
| 3.60 || not present | |||
|} | |||
Runs the entrypoint of all modules in provided list. The list end is marked by an entry with <code>moduleId = SCE_UID_INVALID_UID</code>. | |||
<source lang="C"> | <source lang="C"> | ||
// If run_boot_entry is SCE_TRUE, module_start is executed on core 0 then | |||
// module_bootstart is executed on all cores. Otherwise, module_start is executed on all cores and | |||
// module_bootstart is not executed. | |||
SceInt32 BootModules(SceNskblModuleInfo* module_list, SceSize args, const void* argp, SceBool run_boot_entry); | |||
</source> | </source> | ||
=== | === sceKernelBootBootModulesForKernel === | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 458: | Line 625: | ||
|} | |} | ||
Temp name was sceKblBootModulesForKernel. | |||
In FW 3.60 this function is at 0x51001571. | In FW 3.60 this function is at 0x51001571. | ||
<source lang="C">int | <source lang="C">int sceKernelBootBootModulesForKernel(SceUID *pUidList, SceUInt32 count, SceSize args, void *argp);</source> | ||
=== | === sceAuthMgrExitForKernel === | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 474: | Line 641: | ||
|} | |} | ||
Temp name was sceKblAuthMgrCloseForKernel. | |||
In FW 3.60 this function is at 0x51001345. | In FW 3.60 this function is at 0x51001345. | ||
<source lang="C">int | <source lang="C">int sceAuthMgrExitForKernel(void);</source> | ||
=== sceKblSetNonSyncModuleStartForKernel === | === sceKblSetNonSyncModuleStartForKernel === | ||
Line 546: | Line 713: | ||
In FW 3.60 this function is at 0x510128AD. | In FW 3.60 this function is at 0x510128AD. | ||
These hardware flags are maybe simply the [[KBL_Param#Hardware_Info_2]] like in [[SceSyscon#sceSysconGetHardwareInfo2ForDriver]]. | |||
int sceKblGetHardwareFlagsForKernel(SceHardwareFlags *pFlags); | <source lang="C">int sceKblGetHardwareFlagsForKernel(SceHardwareFlags *pFlags);</source> | ||
</source> | |||
=== | === sceSdStandaloneInitForKernel === | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 562: | Line 725: | ||
|} | |} | ||
Temp name was sceKblInitDeviceForKernel. | |||
Some device init function. On FW 0.940 it initializes and mounts <code>os0:</code> (eMMC) and <code>sd0:</code> (GCSD). | Some device init function. On FW 0.940 it initializes and mounts <code>os0:</code> (eMMC) and <code>sd0:</code> (GCSD). | ||
Line 568: | Line 731: | ||
In FW 3.60 this function is at 0x5100124D. | In FW 3.60 this function is at 0x5100124D. | ||
<source lang="C">int | <source lang="C">int sceSdStandaloneInitForKernel(void);</source> | ||
=== | === sceSdStandaloneExitForKernel === | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
Line 578: | Line 741: | ||
|} | |} | ||
Temp name was sceKblFreeFileSystemCtxForKernel. | |||
Cleanup state created by [[NSKBL# | Cleanup state created by [[NSKBL#sceSdStandaloneInitForKernel|sceSdStandaloneInitForKernel]]. | ||
In FW 3.60 this function is at 0x51001321. | In FW 3.60 this function is at 0x51001321. | ||
<source lang="C">int | <source lang="C">int sceSdStandaloneExitForKernel(void);</source> | ||
[[Category: | [[Category:ARM]] | ||
[[Category:Kernel]] | [[Category:Kernel]] | ||
[[Category:Library]] |
Latest revision as of 17:42, 30 March 2024
The Non-Secure Kernel Boot Loader (NSKBL) is the first program that runs on ARM cores in Non-Secure state. As its name indicate, it has the role of initializing the Non-Secure state environment and loading the kernel from storage.
Module
NSKBL is made of stripped down versions of some Non-Secure kernel modules along with code using all these functions to prepare the non-secure environment and load the next stage.
Reduced versions of the following modules are embedded in NSKBL:
- SceSysmem
- SceKernelModulemgr
- SceExcpmgr
- SceKernelIntrMgr
- SceSblAuthMgr
- SceSblSmschedProxy
- SceLowio (only parts of the Pervasive submodule)
- SceSdif
iofilemgr_if
serves as a thin wrapper around SceSdif for compatibility- Only
sceIoOpen
,sceIoLseek
,sceIoRead
andsceIoClose
are provided - This is just a wrapper. SceIofilemgr is not included in NSKBL!
- Only
It is very likely that the embedded modules in NSKBL are built from the same source tree as the Non-Secure kernel modules. This can be inferred from debugging information contained in some modules (line numbers in NSKBL and module equivalent match).
Notes
How to debug NSKBL
NSKBL supports sd0: for debugging. pKblParam->boot_type_indicator_1 = 0x40000 is required.
sceIoOpen(?) error code 0x803FF007
This error can occur if the file is fragmented.
NSKBL walk through
This section aims to provide an overview of the tasks performed by NSKBL. This description is based on System Software version 3.50 and later. Older versions may behave differently. See also NSKBL Subroutines.
Reset
The NSKBL reset vector, located at 0x51000000
, is the first instruction executed in Non-Secure state, with the MMU turned off.
It jumps to a small ASM function that disables interrupts (cpsid if
), clears exclusive access requests (clrex
), invalidates all caches, sets up stacks for all processors modes, and calls boot
.
boot
This function is responsible for turning on the MMU. It partially initializes the SceKernelBootParam
, copies the KBL Param from 0x40200100
(where it was placed by SKBL) and prints the Starting PSP2 Kernel Boot Loader (Non-secure)
message.
The SceKernelBootParam
initialization includes allocating physical and virtual pages for things like page tables, TTBR, fixed heaps, Sysroot, PhyPageTable, etc. The virtual address for the smaller mappings is randomized in the first 1 MiB of the address space (255 page-sized slots - first slot is reserved for NULL page), while larger allocations are randomized in the first 16 MiB (15 section-sized slots - slot 0 is reserved for the small ASLR mappings). A few mappings are exempted from randomization, like the identity mapping of 0x51000000
-0x51FFFFFF
where NSKBL resides. This memory range is mapped as RWX and seems to be the only RWX mapping in the Non-Secure kernel.
After all these mappings are complete, the MMU is turned on along with caches, branch predictor, and a few other things via the SCTLR
. At this point, boot
is done and sceKblMain
is called.
sceKblMain
This function runs with the MMU enabled and receives a partially filled-in SceKernelBootParam
from boot()
.
This function begins with a call to sceKernelSysrootStart
which initializes the Sysroot structure. The slack space after the structure in the SceSysroot
mapping (which is 16KiB-sized) is turned into a "heap" as part of this process. Memory allocation needs are serviced from this space using sceKernelSysrootAlloc
. At this point, there are no other kernel heaps available. Sysroot status is set to 1 by this function.
After the Sysroot is initialized, sceKernelSysmemStart
is called, which initializes the embedded SceSysmem module. It is at this stage that all the Sysmem object classes are created. Sysroot status is set to 2 then 3 by this function. Kernel heaps are available after the Sysroot status is set to 3 i.e. when this function returns.
Now that Sysmem is initialized, 16-KiB-sized SceKernelBootStackCoreX
memory blocks are allocated, and execution is transfered to main2
after switching the stack pointer to these memblocks. Up until now, the stack it used was located in NSKBL .bss
section.
main2
The main2
function receives a filled-up SceKernelBootParam
from sceKblMain()
.
After setting the Sysroot status to 4, the remaining embedded modules are started (in order: sceKernelModulemgrStart()
, SceExcpmgr start, sceKernelIntrMgrStart
, sceSblAuthMgrStart()
and SceSblSmschedProxy start). Once the embedded modules are started, Sysroot status is set to 0x10 and the final NSKBL function is called.
Final function
The final function receives the same SceKernelBootParam
as main2
.
It first calls sceSdStandaloneInit then loads and caches the gcauthmgr
, rmauth
and encdec_w_portability
security modules.
After this, it loads os0:kd/psp2bootconfig.skprx
, links the SceKblForKernel library to the module, and starts it with SceKernelBootParam
address as argp
.
NSKBL has now handed over control and simply remains resident until it is unmapped by SceSysStateMgr. See also SceKernelModulemgr#sceKernelFinalizeKblForKernel.
Types
typedef struct KBPMemoryRegion { SceUIntPtr pbase; SceSize psize; } KBPMemoryRegion; typedef struct KBPMappingInfo { char *name; //<! Name of the mapping SceUIntPtr pbase; //<! Base physical address of the mapping SceUIntPtr vbase; //<! Base virtual address of the mapping SceSize psize; //<! Size of the physical range of the mapping?? SceSize vsize; //<! Size of the virtual range of the mapping?? - Usually equal to psize, but may be bigger. SceUInt32 extraHigh; //<! Sometimes used as extraHigh for some memblock allocations? Exact meaning unknown. // psize + extraHigh must always be equal to vsize } KBPMappingInfo; typedef struct KBPBootStackInfo { unsigned unk0[2]; SceUIntPtr ttbcr; SceUInt32 dacr; SceUInt32 asid; SceSize size; SceUID blkId; //<! UID of the stack's memblock void *stackBottom; //<! = memblock.vbase + size } KBPBootStackInfo; // This structure is passed as argp of modules like sysmem.skprx, etc // 0x51030100 on NSKBL typedef struct SceKernelBootParam { // Size is 0x310-bytes in 3.60-3.65 and 4.00. Layout appears to be identical. SSceSize size; //<! Size of this structure SceBool secure; //<! Is secure state? Always 0 in NSKBL SceUInt32 num_memory; //<! Number of filled elements in the next field KBPMemoryRegion memory[4]; //<! Physical memory ranges usable by NSKBL and kernel SceKblParam *pKblParam; //<! Pointer to KBL Param unsigned unk30[5]; SceUInt32 SoCInfo; //<! = *(u32*)PERVASIVE_MISC SceUInt32 pmisc_unk4; //<! = *(u32*)(PERVASIVE_MISC + 4) SceUInt32 KermitRevision; //<! = SoCInfo & 0x1FFFF SceUInt32 hwDependent[2]; //<! Depends on pKBLParam->hardwareInfo KBPMappingInfo ttbr0; //<! "SceKernelTTBR0", ASLR vbase, psize 0x4000 unsigned ttbr0_maxAddress; //<! Maximal address covered by TTBR0 unsigned sizeTTBR0Address; //<! Size of the address space covered by TTBR0 KBPMappingInfo ttbr1; //<! "SceKernelTTBR1", ASLR vbase, psize 0x4000 unsigned sizeTTBR1Address; //<! Size of the address space covered by TTBR1 void *L2PT000_mapbase; //<! First vaddr mapped by "SceKernelL2PageTable000" KPBMappingInfo reset; //<! "SceKernelReset", vbase 0, psize 0x1000, extraHigh 0x3000 KBPMappingInfo excpEntry; //<! "SceKernelExceptionEntry", ASLR vbase, psize 0x1000, extraHigh 0x1000 KBPMappingInfo l2pt000; //<! "SceKernelL2PageTable000", ASLR vbase, psize 0x1000, extraHigh 0x1000 KBPMappingInfo l2vector; //<! "SceKernelL2Vector", ASLR vbase, psize 0x4000, extraHigh 0x4000 KBPMappingInfo sysroot; //<! "SceSysroot", ASLR vbase, psize 0x4000 KBPMappingInfo fh32b; //<! "SceKernelFixedHeap32B", ASLR vbase, psize 0x10000 KBPMappingInfo fh48b; //<! "SceKernelFixedHeap48B", ASLR vbase, psize 0x10000 KBPMappingInfo fh64b; //<! "SceKernelFixedHeap64B", ASLR vbase, psize 0x10000 KBPMappingInfo fhUIDEntry; //<! "SceKernelFixedHeapUIDEntry", ASLR vbase, psize 0x10000 KBPMappingInfo fhL2Object; //<! "SceKernelFixedHeapForL2Object", ASLR vbase, psize 0x1000, extraHigh 0x1000 KBPMappingInfo unk188; //<! Unused? NOTE: ASLR vbase = randomize in 255 slots, MegaASLR vbase = randomize in 15 slots KBPMappingInfo phypage; //<! "SceKernelPhyPageTable", MegaASLR vbase, psize 0x80000 KBPMappingInfo phypageHigh; //<! "SceKernelPhyPageTableHigh", MegaASLR vbase, psize 0x80000, pbase 0x77F00000 KBPMappingInfo bootkernimg; //<! "SceBootKernelImage", vbase=pbase=0x51000000, psize 0x1000000, extraHigh 0x1000000 KBPMappingInfo hwreg; //<! Unnamed. vbase=pbase=0xE0000000, psize 0x8000000 SceCorelockContext *pCorelock; KBPBootStackInfo bootCpu[4]; //<! bootCpu[X] corresponds to CPUX's info. Stack is pivoted to this when calling main2. SceSysroot *pSysroot; //<! Pointer to Sysroot structure unsigned unk288; //<! Related to L2PageTable000? Always 0. void *pL2PageTable000; //<! Base address of the L2PageTable000 void *resetVector; //<! Goes into VBAR. = excpEntry.vbase + 0x100 SceKernelPhyMemPart *phyMemPartKD; //<! "SceKernelPhyMemPartKD" SceKernelPhyMemPart *phyMemPartTool; //<! "SceKernelPhyMemPartTool". May be NULL PhyPage *pPageKernelReset; //<! PhyPage object describing the pages backing SceKernelReset PhyPage *pPageL2PageTable000; //<! PhyPage object describing the pages backing SceKernelL2PageTable000 PhyPage *pPageSysroot; //<! PhyPage object describing the pages backing SceSysroot PhyPage *pPageTTBR0; //<! PhyPage object describing the pages backing SceKernelTTBR0 PhyPage *pPageTTBR1; //<! PhyPage object describing the pages backing SceKernelTTBR1 PhyPage *pPageL2Vector; //<! PhyPage object describing the pages backing SceKernelL2Vector PhyPage *pPagePhypage; //<! PhyPage object describing the pages backing SceKernelPhyPageTable PhyPage *pPagePhypageHigh; //<! PhyPage object describing the pages backing SceKernelPhyPageTableHigh PhyPage *pPageBootKernelImage; //<! PhyPage object describing the pages backing SceBootKernelImage???? PhyPage *pPageFixedHeap32B; //<! PhyPage object describing the pages backing SceKernelFixedHeap32B PhyPage *pPageFixedHeap48B; //<! PhyPage object describing the pages backing SceKernelFixedHeap48B PhyPage *pPageFixedHeap64B; //<! PhyPage object describing the pages backing SceKernelFixedHeap64B PhyPage *pPageFixedHeapForL2Object; //<! PhyPage object describing the pages backing SceKernelFixedHeapForL2Object SceUIDFixedHeapObject *pFixedHeap32B; SceUIDFixedHeapObject *pFixedHeap48B; SceUIDFixedHeapObject *pFixedHeap64B; SceUIDFixedHeapObject *pFixedHeapForL2Object; PhyPage *pPageUIDHeap; //<! PhyPage object describing the pages backing SceKernelFixedUIDHeap?? SceUIDEntryHeapObject *pUIDHeap; //<! Pointer to SceKernelFixedUIDHeap object L2PageTableObject *pL2PT000Object; //<! L2PageTableObject for SceKernelL2PageTable000 L2PageTableObject *pPhyPageTblL2PTO; //<! L2PageTableObject for table used to map the PhyPageTable (located @ SceKernelL2PageTable000.pbase + 0x400) SceUIDPartitionObject *pPartitionKernel; //<! Object of "SceKernelRoot" partition SceUID uidPartitionKernel; //<! UID of object above (0x10009) SceUInt32 unk2F8[2]; void *data_0x300; void *pPutcharHandler; SceUInt32 minimum_log_level; SceUInt32 magic; //<! 0x7F407C30 } SceKernelBootParam; typedef struct SceNskblModuleInfo { // size is 0xC on FWs 0.940-0.990 char* filename; // Raw SKPRX file name (e.g. "sysmem.skprx"). Modules are loaded either from os0:kd/ or host0:module/. SceUID moduleId; // SCE_UID_INVALID_UID. It gets filled when loading. SceUInt32 loadFlags; // Passed as flags to sceKernelLoadModule. } __attribute__((packed)) SceNskblModuleInfo; typedef struct SceNskblModuleInfo2 { // size is 4 on FW 3.60 const char* filename; } __attribute__((packed)) SceNskblModuleInfo2; typedef struct SceHardwareFlags { // size is 0x10 on FW 3.60 uint32_t data[4]; } __attribute__((packed)) SceHardwareFlags; /* Many pointers are NSKBL heap relationships */ typedef struct SceNskblSysrootInfo { // size is at least 0xC8 on FW 3.60 SceUID unk_0x00; // maybe some PID. ex: 0x10089 int unk_0x04; void *unk_0x08; void *unk_0x0C; void *unk_0x10; void *unk_0x14; void *unk_0x18; void *unk_0x1C; void *unk_0x20; void *unk_0x24; void *unk_0x28; void *unk_0x2C; SceUID unk_0x30; // maybe some PID. ex: 0x1000B const void *unk_0x34; // mapped paddr in vaddr const void *unk_0x38; // mapped paddr in vaddr void *unk_0x3C; int unk_0x40; // ex: 0x80000000 int unk_0x44; // ex: 0x20000000 void *unk_0x48; void *unk_0x4C; void *unk_0x50; void *unk_0x54; void *unk_0x58; void *unk_0x5C; void *unk_0x60; void *unk_0x64; void *unk_0x68; void *unk_0x6C; void *unk_0x70; void *unk_0x74; void *unk_0x78; void *unk_0x7C; void *unk_0x80; void *unk_0x84; void *unk_0x88; void *unk_0x8C; void *unk_0x90; void *unk_0x94; void *unk_0x98; SceUInt32 magic; // 0x 19442EA8 int unk_0xA0; // ex: 0x1000 int unk_0xA4; // ex: 0x1000 int unk_0xA8; // ex: 0x40000 int unk_0xAC; // ex: 0x200000 int unk_0xB0; // ex: 7 int unk_0xB4; int unk_0xB8; // ex: 0x80 sysroot_t *pSysroot; void *unk_0xC0; void *unk_0xC4; // more...? } SceNskblSysrootInfo; // 3.60 SceNskblSysrootInfo *nskbl_sysroot_info = (SceNskblSysrootInfo *)(0x51000000 + 0x138980); // 3.60
Libraries
Known NIDs
Version | Name | World | Visibility | NID |
---|---|---|---|---|
0.940-3.65 | SceKblForKernel | Non-secure | Kernel | 0xD0FC2991 |
SceKblForKernel
NOTE: this library is linked directly to psp2bootconfig
instead of being registered, and can thus only be imported by this module.
sceSDrfpStartForKernel
Version | NID |
---|---|
0.940-0.990 | 0x230456F3 |
3.60 | not present |
sceSDbgSdioStartForKernel
Version | NID |
---|---|
0.940-0.990 | 0x29A8524D |
3.60 | not present |
Requires DIPSW 193.
SceInt32 sceSDbgSdioStartForKernel(void);
sceSDfMgrStartForKernel
Version | NID |
---|---|
0.940-0.990 | 0xAA8005E4 |
3.60 | not present |
sceKblPutcharForKernel
Version | NID |
---|---|
0.940-3.60 | 0x08E9FAEB |
This is a guessed name.
This function is at 0x510172BD in FW 3.60 and at 0x51003BE0 in FW 0.940.040.
int sceKblPutcharForKernel(void *args, char c);
sceKernelPrintfForKernel
Version | NID |
---|---|
0.940-3.60 | 0x13A5ABEF |
In FW 3.60 this function is at 0x510137A9.
int sceKernelPrintfForKernel(const char *fmt, ...);
sceKernelPrintfLevelForKernel
Version | NID |
---|---|
0.940 | Not present |
0.990-3.60 | 0x752E7EEC |
In FW 3.60 this function is at 0x51013841.
int sceKernelPrintfLevelForKernel(int level, const char *fmt, ...);
sceKernelGetDebugLevelForKernel
Version | NID |
---|---|
0.940-3.60 | 0xC011935A |
Temp name was sceKblGetMinimumLogLevelForKernel.
In FW 3.60 this function is at 0x51013921.
int sceKernelGetDebugLevelForKernel(void);
sceKernelGetDebugPutcharForKernel
Version | NID |
---|---|
0.940-3.60 | 0x9B868276 |
In FW 3.60 this function is at 0x51013765.
void *sceKernelGetDebugPutcharForKernel(void);
sceKernelSysrootProcessmgrStart2ForKernel
Version | NID |
---|---|
0.940-3.60 | 0x161D6FCC |
In FW 3.60 this function is at 0x510123DD.
int sceKernelSysrootProcessmgrStart2ForKernel(void);
sceKernelSysrootThreadMgrStartAfterProcessForKernel
Version | NID |
---|---|
0.940-3.60 | 0x1DB28F02 |
In FW 3.60 this function is at 0x510123A1.
int sceKernelSysrootThreadMgrStartAfterProcessForKernel(void);
sceKernelSysrootIofilemgrStartForKernel
Version | NID |
---|---|
0.940-3.60 | 0xC7B77991 |
In FW 3.60 this function is at 0x5101297D.
int sceKernelSysrootIofilemgrStartForKernel(void);
sceKernelSysrootCorelockUnlockForKernel
Version | NID |
---|---|
0.940-3.60 | 0x314AA770 |
In FW 3.60 this function is at 0x510124FD.
void sceKernelSysrootCorelockUnlockForKernel(void);
sceKernelSysrootCorelockLockForKernel
Version | NID |
---|---|
0.940-3.60 | 0x807B4437 |
In FW 3.60 this function is at 0x510124E5.
void sceKernelSysrootCorelockLockForKernel(SceUInt32 core);
sceKblCpuSuspendIntrForKernel_old
Version | NID |
---|---|
0.940-0.990 | 0x99B2F981 |
3.60 | not present |
This is a guessed name.
On FW 0.940, it calls a routine that simply executes cpsid i
then returns 0.
CPSID i ; Disable all interrupts except NMI (set PRIMASK)
Disables IRQ interrupts by setting the I-bit in the CPSR.
sceKblCpuSuspendIntrForKernel_new
Version | NID |
---|---|
0.940-0.990 | Not present |
3.60 | 0xDDB3A1A8 |
This is a guessed name. Temp name was sceKblCpuSwitchInterruptsForKernel, sceKblCpuDisableIrqInterruptsForKernel.
In FW 3.60 this function is at 0x51003554.
void sceKblCpuSuspendIntrForKernel_new(void);
sceSblAimgrIsCEXForKernel
Version | NID |
---|---|
0.940-3.60 | 0x8A416887 |
In FW 3.60 this function is at 0x510171B5.
int sceSblAimgrIsCEXForKernel(void);
sceSblAimgrIsDiagForKernel
Version | NID |
---|---|
0.940-3.60 | 0xC3DDDE15 |
In FW 3.60 this function is at 0x51017175.
int sceSblAimgrIsDiagForKernel(void);
sceSblAimgrIsDEXForKernel
Version | NID |
---|---|
0.940-0.990 | Not present |
3.60 | 0x5945F065 |
In FW 3.60 this function is at 0x51017159.
int sceSblAimgrIsDEXForKernel(void);
sceSblAimgrIsToolForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0xB6C9ACF1 |
In FW 3.60 this function is at 0x51017139.
int sceSblAimgrIsToolForKernel(void);
sceSblAimgrIsTestForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x943E7537 |
In FW 3.60 this function is at 0x5101711D.
int sceSblAimgrIsTestForKernel(void);
sceSblAimgrIsVITAForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x838466E9 |
In FW 3.60 this function is at 0x51017299.
int sceSblAimgrIsVITAForKernel(void);
sceSblAimgrIsDolceForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0xA7BD4417 |
In FW 3.60 this function is at 0x510172A1.
int sceSblAimgrIsDolceForKernel(void);
sceSblAimgrIsGenuineDolceForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0xB6D00D6D |
In FW 3.60 this function is at 0x510171E5.
int sceSblAimgrIsGenuineDolceForKernel(void);
LoadModulesForKernel
Version | NID |
---|---|
0.940-0.990 | 0xFAE33FDD |
3.60 | not present |
Load all modules from the provided list. The list end is marked by an entry with moduleName = NULL
.
Module GUIDs are populated into the list, so it must be writeable.
SceInt32 LoadModules(SceNskblModuleInfo* module_list);
sceKernelBootLoadModulesForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x6D7A1F18 |
Temp name was sceKblLoadModulesForKernel.
In FW 3.60 this function is at 0x51001551.
int sceKernelBootLoadModulesForKernel(const SceNskblModuleInfo2 *pList, SceUID *pUidList, SceUInt32 count, SceBool use_tool_extended_memory);
BootModulesForKernel
Version | NID |
---|---|
0.940-0.990 | 0xA7D60F71 |
3.60 | not present |
Runs the entrypoint of all modules in provided list. The list end is marked by an entry with moduleId = SCE_UID_INVALID_UID
.
// If run_boot_entry is SCE_TRUE, module_start is executed on core 0 then // module_bootstart is executed on all cores. Otherwise, module_start is executed on all cores and // module_bootstart is not executed. SceInt32 BootModules(SceNskblModuleInfo* module_list, SceSize args, const void* argp, SceBool run_boot_entry);
sceKernelBootBootModulesForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x9A92436E |
Temp name was sceKblBootModulesForKernel.
In FW 3.60 this function is at 0x51001571.
int sceKernelBootBootModulesForKernel(SceUID *pUidList, SceUInt32 count, SceSize args, void *argp);
sceAuthMgrExitForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x79241ACF |
Temp name was sceKblAuthMgrCloseForKernel.
In FW 3.60 this function is at 0x51001345.
int sceAuthMgrExitForKernel(void);
sceKblSetNonSyncModuleStartForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0x9F4F3F98 |
This is a guessed name.
In FW 3.60 this function is at 0x51001561.
int sceKblSetNonSyncModuleStartForKernel(void);
sceKernelCpuIdForKernel
Version | NID |
---|---|
0.940-3.60 | 0xB506A10E |
In FW 3.60 this function is at 0x510147C9.
int sceKernelCpuIdForKernel(void);
sceKernelCheckDipswForKernel
Version | NID |
---|---|
0.990-3.60 | 0xC8F4DE71 |
In FW 3.60 this function is at 0x51015851.
int sceKernelCheckDipswForKernel(int bit);
sceSblQafManagerIsAllowKernelDebugForKernel
Version | NID |
---|---|
0.940-3.60 | 0xCE94F329 |
In FW 3.60 this function is at 0x51016FD1.
int sceSblQafManagerIsAllowKernelDebugForKernel(void);
sceKblGetHardwareFlagsForKernel
Version | NID |
---|---|
0.990 | not present |
3.60 | 0xD3A516D5 |
This is a guessed name.
In FW 3.60 this function is at 0x510128AD.
These hardware flags are maybe simply the KBL_Param#Hardware_Info_2 like in SceSyscon#sceSysconGetHardwareInfo2ForDriver.
int sceKblGetHardwareFlagsForKernel(SceHardwareFlags *pFlags);
sceSdStandaloneInitForKernel
Version | NID |
---|---|
0.940-3.60 | 0xF7AF8690 |
Temp name was sceKblInitDeviceForKernel.
Some device init function. On FW 0.940 it initializes and mounts os0:
(eMMC) and sd0:
(GCSD).
In FW 3.60 this function is at 0x5100124D.
int sceSdStandaloneInitForKernel(void);
sceSdStandaloneExitForKernel
Version | NID |
---|---|
0.940-3.60 | 0x261F2747 |
Temp name was sceKblFreeFileSystemCtxForKernel.
Cleanup state created by sceSdStandaloneInitForKernel.
In FW 3.60 this function is at 0x51001321.
int sceSdStandaloneExitForKernel(void);