NSKBL: Difference between revisions

From Vita Development Wiki
Jump to navigation Jump to search
No edit summary
 
(16 intermediate revisions by 3 users not shown)
Line 1: Line 1:
Non-Secure Kernel Boot Loader (NSKBL) is a Non-Secure world program that performs eMMC setup, base kernel modules loading, etc. during PSVita boot.
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 ==


The NSKBL contains subroutines that are stripped versions of the non-secure kernel ones found in [[SceSysmem]], [[SceKernelModulemgr]], [[SceSblSmschedProxy]], [[SceExcpmgr]], [[SceKernelIntrMgr]], [[SceSblAuthMgr]], [[SceProcessmgr]] (maybe), [[SceSdif]], [[SceIofilemgr]] (simple version?), and some other core drivers.
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* moduleName;    //Raw SKPRX name (e.g. "sysmem.skprx") - modules are loaded either from os0:kd/ or host0:module/
    SceUIntPtr pbase;
     SceUID moduleId;    //SCE_UID_INVALID_UID, gets filled when loading
    SceSize psize;
     SceUInt32 loadFlags; //Passed as flags to sceKernelLoadModuleWithoutStart
} 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>
=== SceKblForKernel_99B2F981 ===
{| class="wikitable"
|-
! Version !! NID
|-
| 0.940-0.990 || 0x99B2F981
|-
| 3.60 || not present
|}
On FW 0.940, it calls a routine that simply executes <code>cpsid i</code> then returns 0.
=== 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">
// If run_boot_entry is SCE_TRUE, module_start is executed on core 0 and
// module_bootstart is executed on all cores
SceInt32 BootModules(SceNskblModuleInfo* module_list, SceSize args, const void* argp, SceBool run_boot_entry);
</source>


=== sceSDfMgrStartForKernel ===
=== sceSDfMgrStartForKernel ===
Line 159: Line 295:
| 3.60 || not present
| 3.60 || not present
|}
|}
=== 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>


=== sceKblPutcharForKernel ===
=== sceKblPutcharForKernel ===
Line 224: Line 344:
|}
|}


Temp name was sceKblGetMinimumLogLevel.
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>


=== sceKblCpuDisableIrqInterruptsForKernel ===
=== 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 sceKblCpuDisableIrqInterruptsForKernel(void);</source>
<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>


=== sceKblLoadModuleForKernel ===
=== 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:
|}
|}


This is a guessed name.
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">
typedef struct SceModuleLoadList {
// If run_boot_entry is SCE_TRUE, module_start is executed on core 0 then
  const char *filename;
// module_bootstart is executed on all cores. Otherwise, module_start is executed on all cores and
} __attribute__((packed)) SceModuleLoadList;
// module_bootstart is not executed.
 
SceInt32 BootModules(SceNskblModuleInfo* module_list, SceSize args, const void* argp, SceBool run_boot_entry);
int sceKblLoadModuleForKernel(const SceModuleLoadList *pList, SceUID *pUidList, SceUInt32 count, SceBool use_tool_extended_memory);
</source>
</source>


=== sceKblStartModuleForKernel ===
=== sceKernelBootBootModulesForKernel ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 458: Line 625:
|}
|}


This is a guessed name.
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 sceKblStartModuleForKernel(SceUID *pUidList, SceUInt32 count, SceSize args, void *argp);</source>
<source lang="C">int sceKernelBootBootModulesForKernel(SceUID *pUidList, SceUInt32 count, SceSize args, void *argp);</source>


=== sceKblAuthMgrCloseForKernel ===
=== sceAuthMgrExitForKernel ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 474: Line 641:
|}
|}


This is a guessed name.
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 sceKblAuthMgrCloseForKernel(void);</source>
<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.


<source lang="C">
These hardware flags are maybe simply the [[KBL_Param#Hardware_Info_2]] like in [[SceSyscon#sceSysconGetHardwareInfo2ForDriver]].
typedef struct SceHardwareFlags {
uint32_t data[4];
} __attribute__((packed)) SceHardwareFlags;


int sceKblGetHardwareFlagsForKernel(SceHardwareFlags *pFlags);
<source lang="C">int sceKblGetHardwareFlagsForKernel(SceHardwareFlags *pFlags);</source>
</source>


=== sceKblInitDeviceForKernel ===
=== sceSdStandaloneInitForKernel ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 562: Line 725:
|}
|}


This is a guessed name.
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 sceKblInitDeviceForKernel(void);</source>
<source lang="C">int sceSdStandaloneInitForKernel(void);</source>


=== sceKblFreeFileSystemCtxForKernel ===
=== sceSdStandaloneExitForKernel ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 578: Line 741:
|}
|}


This is a guessed name.
Temp name was sceKblFreeFileSystemCtxForKernel.


Cleanup state created by [[NSKBL#sceKblInitDeviceForKernel]].
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 sceKblFreeFileSystemCtxForKernel(void);</source>
<source lang="C">int sceSdStandaloneExitForKernel(void);</source>
 


[[Category:Modules]]
[[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:

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);