NSKBL

From Vita Development Wiki
Revision as of 17:42, 30 March 2024 by CelesteBlue (talk | contribs) (→‎sceKblGetHardwareFlagsForKernel)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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