Iplloader
The PRE-IPL called "Lib-PSP iplloader" internally by sony is mapped to 0xBFC00000 which is the reset vector of PSP's MIPS R4000 CPU, on retail PSP units it is the Tachyon (Allegrex MIPS R4000 based SOC) boot rom, on DTP-T1000 it is loaded externally to volatile memory mapped at 0xBFC00000.
0.7.0 Lib-PSP iplloader and onward are composed of two parts: A loader from 0xbfc00000 to 0xbfc0027F and a payload starting at 0xbfc00280 and ending at the size specified at 0xbfc000034 (little endian 0x2 bytes).
PS Vita's Lib-PSP iplloader from version 0.996 and onward's payload start at 0xbfc00180 rather than 0xbfc00280 on previous and PSP hardware versions.
A few example of payload sizes and their hashes are:
SDK Version | Date (DD-MM-YYYY) | Payload Size | Hash |
---|---|---|---|
Tachyon 0x00140000-0x00300000 | 20-04-2004 (build date in ROM) | 0xAF8 | SHA-256: 48F4F11C383621C8569EC07273AE0AF6AD79681CF5B77263A69CF908EEFE4A53 (ROM) |
0.4.0 | 23-07-2004 (or older/last modified date for kbooti.bin) | n/a | SHA-256: 18B5BF7AEFE956D99B397AAAAC94DC965ADFDBC2BE0532096BBC1F8F8C5B7C34 (Full Binary) |
0.6.0 | 08-09-2004 (or older/last modified date for kbooti.bin) | n/a | SHA-256: 5CDEDDEBE11807DDAEB17BAC03945A0B828E8057C9587652CA207E3BB959AC96 (Full Binary) |
0.7.0 | 18-09-2004 (build date in the payload) | 0x894 | SHA-256: 351ECD64C945489999D477ECAFBFBB8FE769C2484636D2F7323557F7EEFD54A2 (payload only)
SHA-256: 388FA1DB87973A2A37D576AAAB785D840CA4D883AB5111781DA2D0AF59CFE667 (Full decrypted binary) |
0.9.0 | 15-10-2004 (build date in the payload) | 0x894 | SHA-256: 4F794E4FF32D5267AEAEDBA362D005EF0B7E93E29CF7C8209E0D9DBB0144F4DB (payload only)
SHA-256: E415198C16E29D96C9232FF78272EE639D0630A56E370ED18A33D358FEF7CA95 (Full decrypted binary) |
Tachyon 0x00400000-0x00500000 | 04-01-2005 (build date in ROM) | 0xB30
SHA-256: 41B2578F84BDE33E09356F0170FF99E2417EA7B1D02BD9163A41AE61FE74C3A5 (ROM) | |
2.6.0 | 22-10-2005 (build date in the payload) | 0xBF4 | SHA-256: 8821D96F5FB35C55DF649A97F5703F8A705362C2F54665B5EE4221E686B5578A (payload only)
SHA-256: 0A83CB36F1FE7C2A9A53BD46E6FFD915D4D1BB97EED3D1EF336960DB752C3446 (Full decrypted binary) |
2.7.1 | 14-02-2006 (build date in the payload) | 0xBF4 | SHA-256: F9160C03EC6174F54F1C1EB645CFBBDB65B3DA47DA1A5478BE30E5EB2B0852B4 (payload only)
SHA-256: 7DDFF7093906C10BA11D7402E9939763173F1ADEA59A38B4006484FD18EA21EA (Full decrypted binary) |
3.5.0 | 01-06-2007 (last modified date for bootdispi.bin) | 0xC74? | ?? |
0.931-0.995 (Inside the PS Vita Compat Secure Module) | 11-17-2010 (last modified date for 0.940I compat_sm.self) | 0x2C0 | SHA-256: 6D75EC720739C53228B1CA1AFF6CE073AE542BBB38FCC9B8710EC5EB3889B942 (Full Binary) |
0.996.070-0.996.090 (Inside the PS Vita Compat Secure Module) | 07-22-2011 (last modified date for compat_sm.self) | 0xD34 | SHA-256: E09B36DE655A441D2C94D39EF7BBC505EAB1722E9380EBE73E9E6A7DC88D9731 (Full Binary) |
1.000.041-1.06 (Inside the PS Vita Compat Secure Module) | 08-30-2011 (last modified date for compat_sm.self) | 0xDB4 | SHA-256: C2AE6939BC4B06CB4A81415E27EB1E7129C561B9C16C3AAFF6FDEBBAB48EBD09 (Full Binary) |
1.50-1.81 (Inside the PS Vita Compat Secure Module) | 12-14-2011 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 522851781DD82F89D69EBFE0F25C3E7CFE5899A53E850F2F979AD1B0E53376F9 (Full Binary) |
2.00-2.05 (Inside the PS Vita Compat Secure Module) | 11-16-2012 (last modified date for compat_sm.self) | 0xE34 | SHA-256: A9A097ED8925B83A210202AA4C943011C05FE48028BA6E05E85E1494143B0100 (Full Binary) |
2.06-2.12 (Inside the PS Vita Compat Secure Module) | 02-22-2013 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 25E22C1D988609AA948F103E1312297F9533CA689B3C1BDC2CECBBC43997D566 (Full Binary) |
2.50-3.00 (Inside the PS Vita Compat Secure Module) | 06-27-2013 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 187DD28ADAD4167F3849392D570CA5A56DEEC608156D0EC6F2453958B1DB9672 (Full Binary) |
3.10-3.18 (Inside the PS Vita Compat Secure Module) | 12-05-2013 (last modified date for compat_sm.self) | 0xE34 | SHA-256: EF7D498295E416CCBD79FED78656E683DA1DCBC7B88C521DF0A1E00F5EC450FE (Full Binary) |
3.30-3.35 (Inside the PS Vita Compat Secure Module) | 09-25-2014 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 7DF591C05BF66292B6868CE4331A1DC11B7A0E421D082971E9CA65C7236B6843 (Full Binary) |
3.36-3.35 (Inside the PS Vita Compat Secure Module) | 01-09-2015 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 7E66976C311F5D3797A30B09DC608A0FA2E67EAA060097423CFD2E5FC89A57D9 (Full Binary) |
3.51-3.55 (Inside the PS Vita Compat Secure Module) | 05-12-2015 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 6E869E08CCE41E0AA0D386DE8936F81F66CABB20B964C3EF2159548852F39F30 (Full Binary) |
3.57-3.63 (Inside the PS Vita Compat Secure Module) | 11-25-2015 (last modified date for compat_sm.self) | 0xE34 | SHA-256: 047366634210449C62FD813B3BFDA6267A1FA8683BA17901A214FC32E473A35F (Full Binary) |
3.65-3.70 (Inside the PS Vita Compat Secure Module) | 03-17-2017 (last modified date for compat_sm.self) | 0xE34 | SHA-256: B5EF4FB2C84D629B2BDC9A70A4B8E5A7EC31CD9EA330E309361C80A9A96B65C5 (Full Binary) |
Devkit behavior
bloadp
bloadp is used to send kbooti binaries (encrypted pre-ipl data from 0x0 to 0x1000 and IPL blocks at 0x1000) to the DTP-T1000. bloadp will (through tachsm) initialize the 0x1D600000 memory (from /dev/mem or 0x00000000 from /dev/tachsm0 ; /dev/tachsm1 is mapped from 0x1D400000 to 0x1D5FFFFF) on the Communication Processor side (0x200000 in size, 0x100000 on the tachsm0 device) which is the DTP-T1000 PSP Shared memory, on the psp/tachyon side it is mapped to 0xBFE00000, it will then write the kbooti binary at 0x1D600000, however on SDK 0.5.0 and below dstdb (which bloadp was originally a part of), it was possible to specify the address to which kbooti would be loaded (default address to be specified was 0xbfc00000, the allowed range was 0xbfc00000-0xbfc10000), this in effect would write kbooti to 0x1D600000/0xBFE00000 + the address specific in the range -0xbfc00000, so for instance if 0xbfc20000 was specified, this would write kbooti to 0xBFE20000, keep in mind that the actual 0xBFC00000 memory mirrors itself every 0x1000 segments. If bloadp is never invoked, 0x1D700000 to 0x1D7FFFFF is uninitialized memory, it is cleared out as soon as bloadp is invoked.
Prototype 0.4.0-0.6.0 (23-07-2004 or older)
Prototype Lib-PSP iplloader will read the IPL blocks in place from 0xBFE01000 and decrypts them to 0x88400000 before jumping there, IPL blocks have no metadata in prototype IPLs instead Lib-PSP iplloader uses 0x88400000 as a hardcoded entry point (see code samples below), in fact the IPL payload/part3, nested inside current IPL revisions and loaded by main.bin are using the prototype format, so the prototype Lib-PSP iplloader loads the IPL part 3/payload directly. It is also of worthy of note that prototype Lib-PSP iplloader for DEM-1000 (for 0.4.0 and 0.6.0 firmwares) do not make use of 0xa0010000 as their payload location, the "payload" is instead executed in place at 0xbfc00000, as such the Lib-PSP iplloader data is not wiped and can be dumped in its entirety using a custom IPL (please note that the IPL format is different than later revision IPLs, prototype IPLs are loaded in a single raw kirk cmd0 0x01 block)
// 0.4 Lib-PSP iplloader } // 0xbfc00138 function_bfc00178(100); uint32_t v9 = function_bfc00550(0x8400000, 0x1fe01000); // 0xbfc0014c // branch -> 0xbfc00154 while (v9 < 0) { // 0xbfc00154 // continue -> 0xbfc00154 } // 0xbfc00158 return unknown_88400000(-0x78000000, v8); }
// 0.6 Lib-PSP iplloader // 0xbfc00168 function_bfc001b0(100); uint32_t v13 = function_bfc006b0(0x8400000, 0x1fe01000); // 0xbfc0017c // branch -> 0xbfc00184 while (v13 < 0) { // 0xbfc00184 // continue -> 0xbfc00184 } // 0xbfc00188 return unknown_88400000(-0x78000000, v9, v10, v1); }
0.7.0-2.50 (18-09-2004)
The 0.7.0+ pre-ipl will copy it's Lib-PSP iplloader payload (stored at 0xBFC000280) to 0x80010000 (physical address 0xa0010000) and jump there, because on DTP-T1000 0xbfc00000 is writable during Lib-PSP iplloader execution and because 0xBFD00000 is an invalid range on DTP-T1000, the payload will use the 0xBFC00000 memory (which originally contains the whole Lib-PSP iplloader loader+payload) as work ram; it will however not wipe itself so you can dump the important part (the payload) from 0xa0010000 (assuming you gain execution at IPL time).
IPL blocks are then loaded from 0xBFE01000 by Lib-PSP iplloader and copied to 0xBFC00000 where they are decrypted in place and copied to the location of load address specified in the metadata.
2.60+ (22-10-2005)
Because a hash of the data stored between 0xbfc00040 and 0xbfc002c0 is used in an additional step by 2.60+ IPLs to decrypt main.bin, from 2.60 and onward the payload will overwrite 0xbfc00000 with an identical copy of first 0x2C0 bytes of the original psp-1000(01g) Lib-PSP iplloader (pre-ipl) rom data (stored at 0xBFC00BB0 in the 2.60 and 2.71 kbooti, 0x80010930 in the payload) and wipe everything after 0xbfc002C0 with 00 before jumping to the IPL entrypoint, if bootstrapping the 1.50 firmware using the 2.60/2.71 Lib-PSP iplloader part, data from the retail rom addresses 0xBFC00200 to 0xBFC002C0 is retrievable, please note that this would not have been enough to generate the hash required to decrypt 2.60+ main.bin in any case and dumping using a custom IPL would be required to retrieve enough of the data even on a DTP-T1000 because every Sony IPL performs "*(u32 *)0xBC100004 = -1;)
2.6.0 kbooti also appears to fix one of the pre-ipl flaws that allowed to load the Pandora time attacked block, it likely either checks for the the entrypoint to not be in the 0xb* range or prevents the use of IPL blocks with 0x0 set as a load address (or both), it does not however check for the data size of the block.
The code that copies 0x2C0 bytes from 0x80010930 to 0xbfc0 verbatim from the 2.6.0 pre-ipl payload.
0x800100c4: 19 00 20 13 beq 0x8001012c <entry_point+0x12c>, $zero, 0x8001012c <entry_point+0x12c> 0x800100c8: 00 00 00 00 sll $zero, $zero, 0x0 0x800100cc: 8f 40 00 0c jal 0x8001023c <function_8001023c> 0x800100d0: 00 00 00 00 sll $zero, $zero, 0x0 0x800100d4: 81 40 00 0c jal 0x80010204 <function_80010204> 0x800100d8: 00 00 00 00 sll $zero, $zero, 0x0 0x800100dc: c0 bf 04 3c lui $a0, 0xbfc0 0x800100e0: 21 28 00 00 addu $a1, $zero, $zero 0x800100e4: 00 10 06 24 addiu $a2, $zero, 0x1000 0x800100e8: 5a 41 00 0c jal 0x80010568 <function_80010568> 0x800100ec: 00 00 00 00 sll $zero, $zero, 0x0 0x800100f0: c0 bf 04 3c lui $a0, 0xbfc0 #0xbfc000000 0x800100f4: 01 80 05 3c lui $a1, 0x8001 0x800100f8: 30 09 a5 24 addiu $a1, $a1, 0x930 #0x80010930 0x800100fc: 00 00 06 3c lui $a2, 0x0 0x80010100: c0 02 c6 24 addiu $a2, $a2, 0x2c0 #size of the data (0x2C0) 0x80010104: 50 41 00 0c jal 0x80010540 <function_80010540>
Please note that neither the 0xbfc00000 (on DTP-T1000) nor 0xa0010000 memory locations survive reboots.
3.50+ (01-06-2007 or older)
Lib-PSP iplloader adds a step using a 0x40 bytes xor key to decrypt the CMAC hash and data keys from the IPL block headers, as a result you cannot decrypt the IPL blocks meant for the new Lib-PSP iplloader using kirk cmd 1.
Retail behavior
pseucode from Tachyon 0x00140000-0x00300000 Lib-PSP iplloader payload:
int iplBlockNumber = 0; u32 checksum = 0; // load/decrypt all encrypted ipl blocks while(1) { // copy an encrypted ipl block to 0xBFD00000-0xBFD01000 (4KB embedded cpu ram) if (LoadIplBlock(iplBlockNum ber, block) < 0) while(1); // decrypt the ipl block in place (uh oh...) if (DecryptIplBlock(block, block)) while(1); // first block will have zero as its checksum since there is no previous block (another uh oh...) if (block->checksum != checksum) while(1); // load the 'data' section of the ipl block to the specified address (0x040Fxxxx range) if (block->loadaddr) checksum = memcpy(block->loadaddr, block->data, block->blocksize); // reached the end of the ipl, jump to the entry address (0x040F0000) if (block->entry) { // clear caches Dcache(); Icache(); // jump to ipl - do not return block->entry(); } iplBlockNumber++; }
Tachyon revisions 0x00140000 to 0x00300000 Lib-PSP iplloader pseudo code:
PSP Disassembler Ver.0.20 Copyright(c)2005,2006 BOOSTER incl. elf-lib 0.1r2 copyright (c) 2005 djhuevo :file name '0x80010000.bin',size = 4096 Load 3684 NID's name :Disasm ;copied by pre-ipl bootcode, from 0xbfc00280-0xbfc00d78 ;here code is 0x80010000 to 0x80010af8 ; ;$bfd00000-$bfd00fff : sector read buffer ; ;------------------------------------------------------- ;recovery mode selector ;be240004 GPIO READ REG. ; bit4 : device select ,0=NAND Flash, 1= rec-dev ; ;------------------------------------------------------- ;------------------------------------------------------- ;recovery boot device (rec-dev) HW assign ; ;bd200030 command register 00009007 : read request (write size = 8) 00002200 : read sector buffer (read size=200) 00008004 : ? status (write size = 8 , read size=8) 00004000 : ? status (read size=8) 00007001 : ? (data size=8?) ;bd200034 data register (read / write) ;bd200038 status register ; bit14:read data ready ; bit13:parameter wirte ready ; bit12:transmit finish ? ; bit 9:???? error ; bit 8:read data error ; ;bd20003c ??? bit15:reset device ? ; ;------------------------------------------------------- ;--------------------------------------------------------------------------- ;entry ;--------------------------------------------------------------------------- L80010000: ; ;reset I/O $800106B0(L80010a80) ; lui r8,$bc10 ;80010010[3C08BC10,'...<'] ; if(r8[$68]>>16 == 0) $80010034 ; r9 = r8[$78] ori r9,r9,$0800 ;80010028[35290800,'..)5'] b $80010040 ;8001002C[10000004,'....'] sw r9,$78(r8) ;80010030[AD090078,'x...'] ; ;80010034 lw r9,$7c(r8) ;80010034[8D09007C,'|...'] ori r9,r9,$0010 ;80010038[35290010,'..)5'] sw r9,$7c(r8) ;8001003C[AD09007C,'|...'] addiu r4,0,$a ;80010040[2404000A,'...$'] jal $80010768 ;80010044[0C0041DA,'.A..'] sync ;80010048[0000000F,'....'] ;80010768 ; ;check recovery boot mode switch ; r8 = [$be240004] & 0x10 // GPIO bit 4 ; r9 = $80010194 // NAND read BLOCK entry r10= $80010130 // NAND initialize(read FAT) entry ; if(r8!=0)// $80010080 { r9 = $80010248 // rec-dev read BLOCK entry r10= $80010240 // rec-dev initialize entry } ;80010080 [$80010808] = r9 [$8001080c] = 0x000000000 ; ;call read FAT ; (r10)() ; r23 = 0 ; check sum of last block ; ;READ BLOCK LOOP ; L80010098 r25 = [$80010808] ; read BLOCK entry r4 = [$8001080c] ; block num of read ; ;call read body function ; r2 = (r25)(r4,$bfd000000); if(r2<0) $80010128 ; ;decrypt 1000H block ; r2 = $80010620($bfd00000,$bfd00000) if(r2<0) $80010128 ; ;+000c : check sum of last block ? ; if( [$bfd0000c] != r23) $80010128 ; ;+0000 : distination address ;+0004 : block size ; r4 = [$bfd00000] ; +0000 : top pointer r6 = [$bfd00004] ; +0004 : size if(r4!=0) { ;transmit BLOCK body r23 = $80010688(r4,r16 + $10 , r6) } ; ;+0008 : entry point or continue MARK check ; r25 = r16[8] if(r25==0) //$80010114 { ;L80010114: [$8001080c]++ ; next block goto $80010098 } ; ;cache ? $800102D8() ; ;cache ? $800102A0() ; ;goto IPL entry point ; jalr r25 ;8001010C[0320F809,'.. .'] ; ;80010128 while(1); // HALT ; ;--------------------------------------------------------------------------- ;read IPL FAT ;--------------------------------------------------------------------------- L80010130: r16 =r31 ; ;80010134 ;NAND reset $80010308() ; ;8001013C r17 = $80 // top of IPL-FAT sector ;80010140 ;read IPL FAT r2 = $80010334(r17,8001081c,80010810) if(r2<0) $8001018c ;80010164 r8 = r6[0] r9 = r6[4] r10= r6[8] ;ECC signature if(r9 != $6dc64a38) // $8001018c { r17 += $20 // next IPL sector goto $80010140 } ; jr r16 ;80010184[02000008,'....'] return ;------------------------------------------------------------------------ ;NAND read body ; ;r4 : fat logical ptr (400H bytes lba? ) ; ;------------------------------------------------------------------------ L80010194: [$80010800] = r31 [$80010804] = r4 ; r17 = r5 ;get FAT location r8 = $8001081c ; FAT table r9 = (r4>>2)<<1 r8 += r9 r9 = (u16)r8[0] ; r8 = (r9<<2) | (r4 & 3) ; r16 = r8 << 3 ; * 8 r18 = 0 ;800101CC do { ;read body one r2 = $80010334(r16+r18,r17 + (r18<<9),80010810) if(r2<0) $80010230 r8 = r6[0] r9 = r6[4] r10= r6[8] ;ECC signature if(r9 != $6dc64a38) $80010230 r18++ }while(r18<8); ;80010220 r31 = [$80010800] return r0 L80010230: r31 = [$80010800] return -1 ;------------------------------------------------------------------------ ;rec-dev initialize ;------------------------------------------------------------------------ L80010240: return $800103B4() ;------------------------------------------------------------------------ ;rec-dev read 1000H block ;------------------------------------------------------------------------ L80010248: [$80010$800] = r31 ; r16 = r4 r17 = r5 r18 = 0 ;8001025C do{ r2 = $80010418(r18+0x10+r16<<3 ,r17 + r18<<9) if(r2<0) $8001025c r18++ }while(r18<8); ; r31 = [$80010$800] return r2 ;------------------------------------------------------------------------ ;cache ? ;------------------------------------------------------------------------ L800102A0: mfc0 r8,Config ;800102A0[40088000,'...@'] addiu r9,0,$1000 ;800102A4[24091000,'...$'] dc.l $7d081240 [invalid] ;800102A8[7D081240,'@..}'] sllv r9,r9,r8 ;800102AC[01094804,'.H..'] mtc0 0,TagLo ;800102B0[4080E000,'...@'] mtc0 0,TagHi ;800102B4[4080E800,'...@'] addu r8,0,0 ;800102B8[00004021,'!@..'] ; cache $01,r8($0) ;800102BC[BD010000,'....'] cache $03,r8($0) ;800102C0[BD030000,'....'] addiu r8,r8,$40 ;800102C4[25080040,'@..%'] bne r8,r9,$800102bc ;800102C8[1509FFFC,'....'] nop ;800102CC[00000000,'....'] jr r31 ;800102D0[03E00008,'....'] nop ;800102D4[00000000,'....'] ;------------------------------------------------------------------------ ;cache ? ;------------------------------------------------------------------------ L800102D8: mfc0 r8,Config ;800102D8[40088000,'...@'] addiu r9,0,$800 ;800102DC[24090800,'...$'] dc.l $7d081180 [invalid] ;800102E0[7D081180,'...}'] sllv r9,r9,r8 ;800102E4[01094804,'.H..'] addu r8,0,0 ;800102E8[00004021,'!@..'] ; cache $14,r8($0) ;800102EC[BD140000,'....'] cache $14,r8($0) ;800102F0[BD140000,'....'] addiu r8,r8,$40 ;800102F4[25080040,'@..%'] bne r8,r9,$800102ec ;800102F8[1509FFFC,'....'] nop ;800102FC[00000000,'....'] jr r31 ;80010300[03E00008,'....'] sync ;80010304[0000000F,'....'] ;---------------------------------------------------------------------------- ;NAND reset CMD ;---------------------------------------------------------------------------- L80010308: ;nand cmd [$bd101008] = 0xff ;nand sts while( [$bd101004] & 1 ==0); ; [$bd101014] = 0x01 return ;---------------------------------------------------------------------------- ;NAND Read Sector ; ;r4 : sector ;r5 : data buffer ;r6 : Extra buffer ; ;---------------------------------------------------------------------------- L80010334: ;nand sts while([$bd101004] & 1 == 0); ; [$bd101020] = r4 << 10 [$bd101024] = $301 ;80010354 while([$bd101024] & 1 == 0); ;80010364 if([$bd101028] != 0) return -1 ; lui r8,$bff0 ;80010370[3C08BFF0,'...<'] ; r9 = r8[$900] r10 = r8[$904] r2 = r8[$908] ; r6[0] = r9 r6[4] = r10 r6[8] = r2 ; r9 = r5 r2 = $200 L80010394: do { r10 = r8[0] r2 -= 4 r8 += 4 r9[0] = r10 r9 += 4 }while(r2); ;800103AC return 0 ;---------------------------------------------------------------------------- ;rec-dev initialize ;---------------------------------------------------------------------------- L800103B4: r24 = r31 ; ;rec-dev I/O init : device & clock enable ? $800106B0(L80010ad4) ; ;reset device ? ; [$bd20003c] = $8000 ;800103D4 while( [$bd20003c] & 0x8000); ; $80010530() $80010508() ; ;800103F4 do{ r2 = $800105B8() if(r2<0) continue // $800103f4 ; }while(r2 & $0080 == 0); // $800103f4 ; ; jr r24 ;80010410[03000008,'....'] return 0 ;---------------------------------------------------------------------------- ;rec-dev read sector one ; ;a1:sector address ;a2:buffer ; ;---------------------------------------------------------------------------- L80010418: r14 = r5 ; r24 = r31 ; lui r25,$bd20 ; ;read sector COMMAND ? [$bd200030] = $00009007 ; dc.l $7c0428e0 [invalid] ;8001042C[7C0428E0,'.(.|'] ; r5 >>= 8 r9 = (r4 >> 24) << 24 ; r4 = $00010020 r4 |= r9 ; ;write parameter r2 = $800104C0(r4,r5) if(r2<0) return -1 ;busy wait $80010608() ; ;$8001045c do{ r2 = $800105B8() if(r2<0) return -1 r2 = r2 & $0020 }while((r2 & $0020)==0); ; if(r2 & $0040) return -1 ; ;read buffer COMMAND ? [$bd200030] = $00002200 ; ;read sector data r2 = $800104CC(r14,$200) // r14 == r5 if(r2<0) return -1 ;wait finish r2 = $80010508() if(r2<0) return -1 ;busy wait $80010608() ; goto $800103f4 ; ;800104B8 ; jr r24 ;800104B8[03000008,'....'] return -1 ;---------------------------------------------------------------------------- ;rec-dev : commmand output ? ; ;a1:1st write data ;a2:2nd write data ;---------------------------------------------------------------------------- L800104C0: [$bd200034] = r4 [$bd200034] = r5 goto $80010508 ;---------------------------------------------------------------------------- ;rec-dev read data block ; ;a1:distination pointer ;a2:transmit size ; ;---------------------------------------------------------------------------- L800104CC: do{ do{ r9 = [$bd200038] if(r9 & $0100) return -1; }while(r9 & $4000 == 0); ; r2 = [$bd200034] r5 -= 4 r4[0] = r2 r4 += 4 }while(r5 >=0); ; return 0; ;---------------------------------------------------------------------------- ;wait for TX finish ? ;---------------------------------------------------------------------------- L80010508: do{ r9 = [$bd200038] }while(r9 & $1000 == 0); ; if(r9 & $0300) return -1// $80010528 ; return 0 ;---------------------------------------------------------------------------- ;rec-dev read status ? ;---------------------------------------------------------------------------- L80010530: ; addu r15,r31,0 ;80010530[03E07821,'!x..'] ; lui r25,$bd20 ;80010534[3C19BD20,' ..<'] ; [$bd200030] = $00008004 [$bd200034] = $06100800 [$bd200034] = 0 r2 = $80010508() if(r2<0) return -1 ; $800105A4($80010a1c,8); ; ;get status ? ; r4 = [$80010a1c] r5 = [$80010a20] ; if( (r4>>16)&0x15 != 0) return -1 ; return 0 ;---------------------------------------------------------------------------- ;rec-dev read status ? ; ;a1:buffer ;a2:size ; ;---------------------------------------------------------------------------- L800105A4: [$bd200030] = $00004000 ;read sector body return $800104cc(r4,r5) ;----------------------------------------------------------------------------- ;rec-dev device ready check? ;----------------------------------------------------------------------------- L800105B8: ;r25 = $bd200000 [$bd200030] = $00007001 ;wait do { r9 = [$bd200038] }while(r9 & $0100); if((r9 & $4000)==0) $800105c0 ; r2 = [$bd200034] r0 = [$bd200034] // ? ;800105E0 do{ r9 = [$bd200038] if(r9 & $0100) //$80010600 { L80010600: return -1 } }while( (r9 & $1000)==0); ; return r2 & 0xff; ;---------------------------------------------------------------------------- ;wait for rec-dev busy ;---------------------------------------------------------------------------- L80010608: do { r9 = [$bd200038] }while( (r9 & $2000) == 0); return ;----------------------------------------------------------------------------- ;decrypt 1000H block ;----------------------------------------------------------------------------- L80010620: ; lui r25,$bde0 ;80010620[3C19BDE0,'...<'] ; [$bde00010] = $00000001 ;r8 = r4 dc.l $7ca8e000 [invalid] ;8001062C[7CA8E000,'...|'] [$bde0002c] = r8 ; ;r8 = r5 dc.l $7c88e000 [invalid] ;80010634[7C88E000,'...|'] [$bde00030] = r8 ; [$bde0000c] = $00000001 ;$80010644 do{ r8 = [$bde0001c] }while(r8 & $0011 == 0) ; [$bde00028] = r8 if(r8&$0010) // $80010664 { L80010664: [$bde0000c] = $00000002 ;$8001066C do{ r8 = [$bde0001c] }while(r8 & $0002 == 0) ; [$bde00028] = r8 sync return -1 } ; return [$bde00014] ;---------------------------------------------------------------------------- ;transmit data with calc check sum ; ;arg1: source ;arg2: distination ;arg1: size ; ;return : 32bit check sum (add) ; ;---------------------------------------------------------------------------- L80010688: r2 = 0 ; clear check sum do{ r3 = r5[0] r5 += 4 r6 -= 4 r4[0] = r3 r2 += r3 ; check sum r4 += 4 }while(r6>=0); return ;--------------------------------------------------------------------------- ;script executer ; ;a1:script pointer ; ;2 word command ; ;+00[31:28] : CMD ;+00[27: 0] : OFFSET ;+04[31: 0] : VALUE ; ;CMD : command : ; 0 : store | [$b0000000 + OFFSET] = VALUE ; 1 : or | [$b0000000 + OFFSET] |= VALUE ; 2 : and | [$b0000000 + OFFSET] &= VALUE ; 3 : wait toL| while( ( [$b0000000 + OFFSET] &= VALUE) != 0) ;(4) : wait toH| while( (~[$b0000000 + OFFSET] &= VALUE) != 0) ; 5 : delay | for(cnt=VALUE*96;cnt;cnt--) ; F : end | return ; ;--------------------------------------------------------------------------- L800106B0: r8 = r4 ;addu r25,r31,0 ; ;800106b8 do{ // read CMD:offset + VALUE r4 = r8[0] r9 = r4 >> 28 r4 = ( (r4 << 4)>>4 ) | $b0000000 r5 = r4[4] ; if(r9==0) //$80010724 { ;80010724 r4[0]=r5 goto $8001071c } if(r9==1)// $8001072c { ;8001072C r4[0] = r4[0] | r5 goto $8001071c } if(r9==2) // $8001073c { r4[0] = r4[0] & r5 goto $8001071c } r1 = 0 if(r9==3) $8001074c ; r1 = $ffffffff ; nor r1,0,0 ;!!!!! buggy code !!!!! ; addiu r10,r10,-$4 ; if(r9==4) $8001074c if(r9==7) $8001074c ;!!!!! buggy code !!!!! ; if(r9==5) //$80010714 { L80010714: $80010768(r5); goto L8001071C } ;default: ; jr r25 ;8001070C[03200008,'.. .'] return; ; ;case end L8001071C: // next script point r8 += 8 }while(1); ; ;4,7 L8001074C: { do{ r9 = (r4[0] ^ r1) & r5 }while(r9!=0); goto $8001071c } ;-------------------------------------------------------------------------- ;delay ;-------------------------------------------------------------------------- L80010768: r1 = ((r4 << 1) + r4)<<5 // * 96 while(r1) r1--; return ;-------------------------------------------------------------------------- ;-------------------------------------------------------------------------- L80010784: nop ;80010784[00000000,'....'] nop ;80010788[00000000,'....'] nop ;8001078C[00000000,'....'] nop ;80010790[00000000,'....'] nop ;80010794[00000000,'....'] nop ;80010798[00000000,'....'] nop ;8001079C[00000000,'....'] nop ;800107A0[00000000,'....'] nop ;800107A4[00000000,'....'] nop ;800107A8[00000000,'....'] nop ;800107AC[00000000,'....'] nop ;800107B0[00000000,'....'] nop ;800107B4[00000000,'....'] nop ;800107B8[00000000,'....'] nop ;800107BC[00000000,'....'] nop ;800107C0[00000000,'....'] nop ;800107C4[00000000,'....'] nop ;800107C8[00000000,'....'] nop ;800107CC[00000000,'....'] nop ;800107D0[00000000,'....'] nop ;800107D4[00000000,'....'] nop ;800107D8[00000000,'....'] nop ;800107DC[00000000,'....'] nop ;800107E0[00000000,'....'] nop ;800107E4[00000000,'....'] nop ;800107E8[00000000,'....'] nop ;800107EC[00000000,'....'] nop ;800107F0[00000000,'....'] nop ;800107F4[00000000,'....'] nop ;800107F8[00000000,'....'] nop ;800107FC[00000000,'....'] ;----------------------------------------------------------------------------- ; ;r31 save buffer ; L80010800: dl 800100B0 ; ;r4 save buffer ; L80010804: dl 0000002E ; ;read sector function entry ; L80010808: dl L80010194 ; ;read block number ; L8001080C: dl L0000002E ; ;NAND Extra buffer ; L80010810: dl FFFFFFFF dl 6DC64A38 dl FFFFFD89 ; ;NAND Data buffer (IPL FAT) ; L8001081c mfhi 0 ;8001081C[00110010,'....'] mflo 0 ;80010820[00130012,'....'] dsllv 0,r21,0 ;80010824[00150014,'....'] dsrlv 0,r23,0 ;80010828[00170016,'....'] mult 0,r25 ;8001082C[00190018,'....'] div 0,r27 ;80010830[001B001A,'....'] nop ;80010834[00000000,'....'] nop ;80010838[00000000,'....'] nop ;8001083C[00000000,'....'] nop ;80010840[00000000,'....'] nop ;80010844[00000000,'....'] nop ;80010848[00000000,'....'] nop ;8001084C[00000000,'....'] nop ;80010850[00000000,'....'] nop ;80010854[00000000,'....'] nop ;80010858[00000000,'....'] nop ;8001085C[00000000,'....'] nop ;80010860[00000000,'....'] nop ;80010864[00000000,'....'] nop ;80010868[00000000,'....'] nop ;8001086C[00000000,'....'] nop ;80010870[00000000,'....'] nop ;80010874[00000000,'....'] nop ;80010878[00000000,'....'] nop ;8001087C[00000000,'....'] nop ;80010880[00000000,'....'] nop ;80010884[00000000,'....'] nop ;80010888[00000000,'....'] nop ;8001088C[00000000,'....'] nop ;80010890[00000000,'....'] nop ;80010894[00000000,'....'] nop ;80010898[00000000,'....'] nop ;8001089C[00000000,'....'] nop ;800108A0[00000000,'....'] nop ;800108A4[00000000,'....'] nop ;800108A8[00000000,'....'] nop ;800108AC[00000000,'....'] nop ;800108B0[00000000,'....'] nop ;800108B4[00000000,'....'] nop ;800108B8[00000000,'....'] nop ;800108BC[00000000,'....'] nop ;800108C0[00000000,'....'] nop ;800108C4[00000000,'....'] nop ;800108C8[00000000,'....'] nop ;800108CC[00000000,'....'] nop ;800108D0[00000000,'....'] nop ;800108D4[00000000,'....'] nop ;800108D8[00000000,'....'] nop ;800108DC[00000000,'....'] nop ;800108E0[00000000,'....'] nop ;800108E4[00000000,'....'] nop ;800108E8[00000000,'....'] nop ;800108EC[00000000,'....'] nop ;800108F0[00000000,'....'] nop ;800108F4[00000000,'....'] nop ;800108F8[00000000,'....'] nop ;800108FC[00000000,'....'] nop ;80010900[00000000,'....'] nop ;80010904[00000000,'....'] nop ;80010908[00000000,'....'] nop ;8001090C[00000000,'....'] nop ;80010910[00000000,'....'] nop ;80010914[00000000,'....'] nop ;80010918[00000000,'....'] nop ;8001091C[00000000,'....'] nop ;80010920[00000000,'....'] nop ;80010924[00000000,'....'] nop ;80010928[00000000,'....'] nop ;8001092C[00000000,'....'] nop ;80010930[00000000,'....'] nop ;80010934[00000000,'....'] nop ;80010938[00000000,'....'] nop ;8001093C[00000000,'....'] nop ;80010940[00000000,'....'] nop ;80010944[00000000,'....'] nop ;80010948[00000000,'....'] nop ;8001094C[00000000,'....'] nop ;80010950[00000000,'....'] nop ;80010954[00000000,'....'] nop ;80010958[00000000,'....'] nop ;8001095C[00000000,'....'] nop ;80010960[00000000,'....'] nop ;80010964[00000000,'....'] nop ;80010968[00000000,'....'] nop ;8001096C[00000000,'....'] nop ;80010970[00000000,'....'] nop ;80010974[00000000,'....'] nop ;80010978[00000000,'....'] nop ;8001097C[00000000,'....'] nop ;80010980[00000000,'....'] nop ;80010984[00000000,'....'] nop ;80010988[00000000,'....'] nop ;8001098C[00000000,'....'] nop ;80010990[00000000,'....'] nop ;80010994[00000000,'....'] nop ;80010998[00000000,'....'] nop ;8001099C[00000000,'....'] nop ;800109A0[00000000,'....'] nop ;800109A4[00000000,'....'] nop ;800109A8[00000000,'....'] nop ;800109AC[00000000,'....'] nop ;800109B0[00000000,'....'] nop ;800109B4[00000000,'....'] nop ;800109B8[00000000,'....'] nop ;800109BC[00000000,'....'] nop ;800109C0[00000000,'....'] nop ;800109C4[00000000,'....'] nop ;800109C8[00000000,'....'] nop ;800109CC[00000000,'....'] nop ;800109D0[00000000,'....'] nop ;800109D4[00000000,'....'] nop ;800109D8[00000000,'....'] nop ;800109DC[00000000,'....'] nop ;800109E0[00000000,'....'] nop ;800109E4[00000000,'....'] nop ;800109E8[00000000,'....'] nop ;800109EC[00000000,'....'] nop ;800109F0[00000000,'....'] nop ;800109F4[00000000,'....'] nop ;800109F8[00000000,'....'] nop ;800109FC[00000000,'....'] nop ;80010A00[00000000,'....'] nop ;80010A04[00000000,'....'] nop ;80010A08[00000000,'....'] nop ;80010A0C[00000000,'....'] nop ;80010A10[00000000,'....'] nop ;80010A14[00000000,'....'] nop ;80010A18[00000000,'....'] ; ;rec-dev status read buffer ? L80010A1C: dl 00000000,00000000 ; nop ;80010A24[00000000,'....'] nop ;80010A28[00000000,'....'] nop ;80010A2C[00000000,'....'] nop ;80010A30[00000000,'....'] nop ;80010A34[00000000,'....'] nop ;80010A38[00000000,'....'] nop ;80010A3C[00000000,'....'] nop ;80010A40[00000000,'....'] nop ;80010A44[00000000,'....'] nop ;80010A48[00000000,'....'] nop ;80010A4C[00000000,'....'] nop ;80010A50[00000000,'....'] nop ;80010A54[00000000,'....'] nop ;80010A58[00000000,'....'] nop ;80010A5C[00000000,'....'] nop ;80010A60[00000000,'....'] nop ;80010A64[00000000,'....'] nop ;80010A68[00000000,'....'] nop ;80010A6C[00000000,'....'] nop ;80010A70[00000000,'....'] nop ;80010A74[00000000,'....'] nop ;80010A78[00000000,'....'] nop ;80010A7C[00000000,'....'] ; ; ;script command : I/O init ; L80010A80: dl 1C100058,00800000 ; [$bc100058] |= 00800000 dl 1C100050,0000608E ; [$bc100050] |= 0000608E dl 2C10004C,FFFFFBF7 ; [$bc10004C] &= FFFFFBF7 dl 1C100078,00000002 ; [$bc100078] |= 00000002 dl 2E240000,FFFFFFEF ; [$be240000] &= FFFFFFEF : GPIO bit4 direction read? dl 1E240040,00000010 ; [$be240040] |= 00000010 : GPIO bit4 pullup enable ? dl 50000000,00000001 ; delay 1 dl 0D500010,00000001 ; [$bd500010] = 00000001 dl 3D500010,00000001 ; while( [$bd500010] & 1) dl 0D500040,00000001 ; [$bd500040] = 00000001 dl F0000000 ; end ; ;script command : rec-dev I/O init ; L80010AD4: dl 3D500010,00000001 ; while( [$bd500010] & 1) dl 1C100054,00000100 ; [$bc100054] |= 00000100 dl 1C100050,00000400 ; [$bc100050] |= 00000400 dl 1C100078,00000010 ; [$bc100078] |= 00000010 dl 2C10004C,FFFFFEFF ; [$bc10004C] &= FFFFFEFF dl F0000000 ; end ; ;------------------------------------------------------------------------------ ;code end ;------------------------------------------------------------------------------
PS Vita Compatibility mode behavior
Lib-PSP iplloader is sent by the compat secure module to the non-secure kernel which writes it to 0xE8100000 (called CompatSharedSram and mapped to the 0xBFC00000 reset vector on the psp/tachyon side) compat_sm will then send a specific 0x40 bytes key to be used for by the vita's Lib-PSP iplloader as a 0x40 bytes XOR mask against the IPL header (the IPL is stored in the pcbc.skprx kernel module). Kirk cmd 1 will then be used on the result. Unlike on actual PSP units, the IPL is decrypted in a single large block rather than in multiple blocks.
The 0x40 bytes key gets updated depending on the the firmware version in use.
A 0x40 bytes XOR mask is also presumably how the PSP-3000 (and newer) as well as the 3.50+ DTP-T1000 security is most likely handled.
Dumper
As of March 21st 2018, a dumper for DTP-T1000 Lib-PSP iplloader has been made available on github (see link below)