Updater: Difference between revisions

From Vita Development Wiki
Jump to navigation Jump to search
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Various components in user, kernel, and secure kernel work together to update the system. All the relevant libraries are documented in this one page because they work close together.
Various components in user, kernel, and secure kernel work together to update the system. All the relevant libraries are documented in this one page because they work close together.


= SceCuiSetUpper =
= Update Process =


== Module ==
The PS Vita updater is composed of various parts:
This is a user application that is found inside the update [[PUP]]. It has not been seen used outside of development units but is included in retail updates. It does not change often between firmware updater versions.
* SceCuiSetUpper
* ScePsp2Swu
* SceSblSsUpdateMgr
* update_service_sm


=== Known NIDs ===
== Initiator ==
{| class="wikitable"
|-
! Version !! Name !! World !! Privilege !! NID
|-
| 2.12
| SceCuiSetUpper || Non-secure || User || 0x8802DAE9
|}
 
== Libraries ==
This module does not export any library.
 
= ScePsp2Swu =
 
== Module ==
This is a user application that is found inside the update [[PUP]], and extracted to ud0:UPDATE/.
 
=== Known NIDs ===
{| class="wikitable"
|-
! Version !! Name !! World !! Privilege !! NID
|-
| 1.69 || ScePsp2Swu || Non-secure || User || 0xF8D9C101
|}
 
== Libraries ==
This module does not export any library.
 
= SceSblUpdateMgr =
 
== Module ==
This module exists only in the non-secure kernel. The SELF can be found in <code>bootfs:update_mgr.skprx</code>.
 
=== Known NIDs ===
{| class="wikitable"
! Version !! Name !! World !! Privilege !! NID
|-
| 1.69 || SceSblUpdateMgr || Non-secure || Kernel || 0xBA91FE90
|-
| 3.60 || SceSblUpdateMgr || Non-secure || Kernel || 0x528F6BF5
|}
 
== Libraries ==
This module exports libraries to both kernel and user.
 
=== Known NIDs ===
{| class="wikitable"
|-
! Version
! Name
! World
! Visibility
! NID
|-
| 0.931-1.69 || SceSblUpdateMgrForKernel || Non-secure || Kernel || 0xC4466E48
|-
| 0.931-1.69 || SceSblUpdateMgrForDriver || Non-secure || Kernel || 0x0E04CD3D
|-
| 0.931-3.60 || SceSblSsUpdateMgr || Non-secure || User || 0x31406C49
|-
| 0.990 || SceSblSsUpdateMgrAdditional || Non-secure || User || 0xBDD7A86F
|}
 
== Types ==
 
<source lang="C">
typedef char SceUpdateMode;
 
typedef struct SceSblUsSpkgInfo { // Size is 0x10 on FW 0.931-0.990
SceSize size; // size of this structure
int version;
int reserved1;
int reserved2;
} SceSblUsSpkgInfo;
 
typedef struct SceKernelSpackageArgs { // Size is 0x2C on FW 0.931
  SceSize arg_size; // Size of this structure
  SceUInt32 package_type;
  void *addr;
  SceSize size;
  SceUInt32 flags;
  char unk[0x8];
  SceUInt32 seq_no;
  SceUInt32 result;
  SceUInt32 progress;
  SceUInt32 written_rates;
} SceKernelSpackageArgs;
</source>
 
== SceSblUpdateMgrForKernel ==
 
SceSblUpdateMgrForKernel functions and NIDs are exactly identical to SceSblUpdateMgrForDriver.
 
== SceSblUpdateMgrForDriver ==
 
=== sceSblUsVerifyPupForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.940 || 0xD593D613
|}
 
<source lang="C">int sceSblUsVerifyPupForDriver(const char *path);</source>
 
=== sceSblUsVerifyPupHeaderForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.940 || 0xBAFCA304
|}
 
<source lang="C">int sceSblUsVerifyPupHeaderForDriver(const char *path);</source>
 
=== sceSblUsVerifyPupSegmentForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.940 || 0xF43372C4
|}
 
<source lang="C">int sceSblUsVerifyPupSegmentForDriver(const char *path);</source>
 
=== sceSblUsVerifyPupSegmentByIdForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.940 || 0xB4AC7684
|}
 
<source lang="C">int sceSblUsVerifyPupSegmentByIdForDriver(const char *path, void *arg2, int seg_id, int arg4);</source>
 
=== sceSblUsVerifyPupWatermarkForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.940 || 0xDD90C4B9
|}
 
<source lang="C">int sceSblUsVerifyPupWatermarkForDriver(const char *path);</source>
 
=== sceSblUsUpdateSpackageForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0xF41138F1
|}
 
<source lang="C">int sceSblUsUpdateSpackageForDriver(int package_type, void *kaddr, SceSize size, uint32_t flags, int *pRequestId);</source>
 
=== sceSblUsInspectSpackageForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0xE7F5A4C0
|}
 
<source lang="C">int sceSblUsInspectSpackageForDriver(int package_type, void *kaddr, SceSize size, uint32_t flags, int *pRequestId);</source>
 
=== sceSblUsExtractSpackageForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x87AC6E73
|}
 
<source lang="C">int sceSblUsExtractSpackageForDriver(int package_type, void *kaddr, SceSize size, uint32_t flags, int *pRequestId);</source>
 
=== sceSblUsAllocateBufferForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x2D69BFDC
|}
 
<source lang="C">int sceSblUsAllocateBufferForDriver(SceSize size, void **kaddr);</source>
 
=== sceSblUsReleaseBufferForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x45B91736
|}
 
<source lang="C">int sceSblUsReleaseBufferForDriver(void *kaddr);</source>
 
=== sceSblUsGetStatusForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x99D57D18
|}
 
<source lang="C">int sceSblUsGetStatusForDriver(int node_type, int requestId, uint32_t *seq_no, uint32_t *result, uint32_t *progress, uint32_t *written_rates);</source>
 
=== sceSblUsGetSpkgInfoForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0xAE7D3BF5
|}
 
<source lang="C">int sceSblUsGetSpkgInfoForDriver(int package_type, SceSblUsSpkgInfo *pInfo);</source>
 
=== sceSblUsGetUpdateModeForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0xEEC71CCC
|}
 
Get UpdateMode from Ernie NVS. See [[SceSblSsMgr#NVS_Areas]].
 
<source lang="C">int sceSblUsGetUpdateModeForDriver(SceUpdateMode *mode);</source>
 
=== sceSblUsSetUpdateModeForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x266820E9
|}
 
Set UpdateMode to Ernie NVS. See [[SceSblSsMgr#NVS_Areas]].
 
<source lang="C">int sceSblUsSetUpdateModeForDriver(SceUpdateMode mode);</source>
 
=== SceSblUpdateMgrForDriver_6ACEF44D ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x6ACEF44D
|}
 
Only return 0.
 
<source lang="C">int SceSblUpdateMgrForDriver_6ACEF44D(void);</source>
 
=== sceSblUsPowerControlForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x64ECC81A
|}
 
<source lang="C">int sceSblUsPowerControlForDriver(int mode, int flag);</source>
 
=== sceSblUsGetApplicableVersionForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-0.990 || 0x7CC73839
|}
 
<source lang="C">
// type must be 1 or 9 else "builtin revoke list cannot be found"
int sceSblUsGetApplicableVersionForDriver(int type, void *versionBuf);
</source>
 
== SceSblSsUpdateMgrAdditional ==
 
=== sceSblUsInformUpdateStartedForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0x1E40A14E
|}
 
<source lang="C">int sceSblUsInformUpdateStartedForUser(int number1, int number2, const char *str, SceSize len);</source>
 
=== sceSblUsInformUpdateOngoingForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0x3A917CCE
|}
 
<source lang="C">int sceSblUsInformUpdateOngoingForUser(int number1, int number2);</source>
 
=== sceSblUsInformUpdateFinishedForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0x4734B987
|}
 
<source lang="C">int sceSblUsInformUpdateFinishedForUser(int number, const char *str, SceSize len);</source>
 
=== sceSblUsSetSwInfoIntForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0xA870D285
|}
 
<source lang="C">int sceSblUsSetSwInfoIntForUser(char *str, SceSize len, SceUInt32 number);</source>
 
=== sceSblUsSetSwInfoStrForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0x8C7255C8
|}
 
<source lang="C">int sceSblUsSetSwInfoStrForUser(char *str1, SceSize len1, char *str2, SceSize len2);</source>
 
=== sceSblUsSetSwInfoBinForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || 0xF157E34A
|}
 
<source lang="C">int sceSblUsSetSwInfoBinForUser(char *str1, SceSize len1, char *str2, SceSize len2);</source>
 
== SceSblSsUpdateMgr ==
 
=== sceSblUsGetUpdateModeForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x8E834565
|}
 
Temp name was sceSblSsUpdateMgrGetBootMode.
 
Get UpdateMode from Ernie NVS. See [[SceSblSsMgr#NVS_Areas]].
 
<source lang="C">int sceSblUsGetUpdateModeForUser(SceUpdateMode *mode);</source>
 
=== sceSblUsSetUpdateModeForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xC725E3F0
|}
 
Temp name was sceSblSsUpdateMgrSetBootMode.
 
Set UpdateMode to Ernie NVS. See [[SceSblSsMgr#NVS_Areas]].
 
<source lang="C">int sceSblUsSetUpdateModeForUser(SceUpdateMode mode);</source>
 
=== sceSblUsPowerControlForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x1825D954
|}
 
Temp name was sceSblSsUpdateMgrSendCommand.
 
<source lang="C">
// Modes:
// 0: reboot target and/or CP with or without CP update. flag 2 = reboot CP, flag 5 = reboot Ernie
// 1: shutdown target. flag 5 = shutdown Ernie
// 2: sceKernelPowerLock(0)
// 3: sceKernelPowerUnlock(0)
// 4: LED ON
// 5: LED OFF
// 6: LED INIT
 
int sceSblUsPowerControlForUser(int mode, int flag);
</source>
 
=== sceSblUsGetSpkgInfoForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x8E3EC2E1
|}
 
Temp name was sceSblSsUpdateMgrGetSpkgInfo.
 
<source lang="C">int sceSblUsGetSpkgInfoForUser(int package_type, SceSblUsSpkgInfo *pInfo);</source>
 
=== sceSblUsVerifyPupForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x6F5EDBF4
|}
 
path max len : 0x3FF bytes
 
<source lang="C">int sceSblUsVerifyPupForUser(const char *path);</source>
 
=== sceSblUsVerifyPupAdditionalSignForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0xB19366CB
|}
 
path max len : 0x3FF, path len >= 0x400 : error
 
<source lang="C">int sceSblUsVerifyPupAdditionalSignForUser(const char *path);</source>
 
=== sceSblUsVerifyPupHeaderForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.940-3.60 || 0x9BE17A06
|}
 
path max len : 0x3FF


<source lang="C">int sceSblUsVerifyPupHeaderForUser(const char *path);</source>
=== sceSblUsVerifyPupSegmentForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xD47FD33E
|}
path max len: 0x3FF bytes
<source lang="C">int sceSblUsVerifyPupSegmentForUser(const char *path);</source>
=== sceSblUsVerifyPupSegmentByIdForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x95FC1A0A
|}
path max len: 0x3FF bytes
Maybe that seg_id is uint64_t and so it might be part of arg2 or arg4
<source lang="C">int sceSblUsVerifyPupSegmentByIdForUser(const char *path, void *arg2, int seg_id, int arg4);</source>
=== sceSblUsVerifyPupWatermarkForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xC6CDEB8D
|}
path max len : 0x3FF bytes
<source lang="C">int sceSblUsVerifyPupWatermarkForUser(const char *path);</source>
=== sceSblUsUpdateSpackageForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x6E8DDAC4
|}
<source lang="C">int sceSblUsUpdateSpackageForUser(int package_type, SceKernelSpackageArgs *args, int *pRequestId);</source>
=== sceSblUsInspectSpackageForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x1A39F6EE
|}
<source lang="C">int sceSblUsInspectSpackageForUser(int package_type, SceKernelSpackageArgs *args, int *pRequestId);</source>
=== sceSblUsExtractSpackageForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xC1792A1C
|}
<source lang="C">int sceSblUsExtractSpackageForUser(int package_type, SceKernelSpackageArgs *args, int *pRequestId);</source>
=== sceSblUsGetExtractSpackageForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x4897AD56
|}
<source lang="C">
// node_type must be 0, 1 or 2
int sceSblUsGetExtractSpackageForUser(int node_type, int requestId, SceKernelSpackageArgs *args);
</source>
=== sceSblUsAllocateBufferForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x4C06F41C
|}
<source lang="C">int sceSblUsAllocateBufferForUser(SceSize size, void **uaddr);</source>
=== sceSblUsReleaseBufferForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xBD677F5A
|}
<source lang="C">int sceSblUsReleaseBufferForUser(void *uaddr);</source>
=== sceSblUsGetStatusForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xF403143E
|}
<source lang="C">int sceSblUsGetStatusForUser(int node_type, int requestId, SceKernelSpackageArgs *args);</source>
=== sceSblUsCheckSystemIntegrityForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0xBED8DFC7
|}
Not implemented: does nothing.
<source lang="C">
int sceSblUsCheckSystemIntegrityForUser(void) {
int state;
if (sceSblACIsSystemProgramForKernel(0) == 0)
return 0x800F022C;
ENTER_SYSCALL(state);
EXIT_SYSCALL(state);
return 0;
}
</source>
<source lang="C">int sceSblUsCheckSystemIntegrityForUser(void);</source>
=== sceSblUsGetApplicableVersionForUser ===
{| class="wikitable"
! Version !! NID
|-
| 0.931-3.60 || 0x3ADD4B7A
|}
<source lang="C">
// type must be 1 or 9 else "builtin revoke list cannot be found"
int sceSblUsGetApplicableVersionForUser(int type, void *versionBuf);
</source>
=== sceSblUsInformUpdateStartedForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0x9FC8E905
|}
<source lang="C">int sceSblUsInformUpdateStartedForDriver(int number1, int number2, const char *str, SceSize len);</source>
=== sceSblUsInformUpdateOngoingForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0xD0CB50AC
|}
<source lang="C">int sceSblUsInformUpdateOngoingForDriver(int number1, int number2);</source>
=== sceSblUsInformUpdateFinishedForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0x2A02DCFB
|}
<source lang="C">int sceSblUsInformUpdateFinishedForDriver(int number, const char *str, SceSize len);</source>
=== sceSblUsSetSwInfoIntForDriver ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0x157AD4AD
|}
<source lang="C">int sceSblUsSetSwInfoIntForDriver(char *str, SceSize len, SceUInt32 number);</source>
=== SceSblSsUpdateMgr_FE930747 ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0xFE930747
|}
<source lang="C">int SceSblSsUpdateMgr_FE930747(const void *src1, int len1, const void *src2, int len2);</source>
=== SceSblSsUpdateMgr_92A8002B ===
{| class="wikitable"
! Version !! NID
|-
| 0.990 || NOT PRESENT
|-
| 3.60 || 0x92A8002B
|}
<source lang="C">int SceSblSsUpdateMgr_92A8002B(const void *src1, int len1, const void *src2, int len2);</source>
= Update Process =
The Vita updater is composed of various parts.
== Initiator ==
The first part is the initiator of the update. The responsibility of the initiator is to copy the update file (<code>PSP2UPDAT.PUP</code>) to the <code>ud0</code> [[Partitions|partition]] and extract <code>psp2swu.self</code> from the update file to <code>ud0:PSP2UPDATE/psp2swu.self</code>. Then it signals the [[Syscon|syscon]] to start in update mode, where <code>psp2swu.self</code> is loaded instead of the usual shell.
The first part is the initiator of the update. The responsibility of the initiator is to copy the update file (<code>PSP2UPDAT.PUP</code>) to the <code>ud0</code> [[Partitions|partition]] and extract <code>psp2swu.self</code> from the update file to <code>ud0:PSP2UPDATE/psp2swu.self</code>. Then it signals the [[Syscon|syscon]] to start in update mode, where <code>psp2swu.self</code> is loaded instead of the usual shell.


Line 629: Line 16:


== ScePsp2Swu ==
== ScePsp2Swu ==
Once the system restarts into update mode, the updater runs. Normally, it runs in GUI mode which is the green screen with the progress bar. If a flag is set by the initiator, the updater will run in CUI mode, which is a text interface that provides more verbose information about the update process.
Once the system restarts into update mode, the updater runs. Normally, it runs in GUI mode which is the green screen with the progress bar. If a flag is set by the initiator, the updater will run in CUI mode, which is a text interface that provides more verbose information about the update process.


Line 634: Line 22:


== SceSblSsUpdateMgr ==
== SceSblSsUpdateMgr ==
This is a kernel module that actually does the work and performs the update. It verifies the [[PUP]] headers as well as the [[PUP#Packages|package]] headers and then flashes the decrypted images directly to the [[eMMC]] with block writes.
 
This is a kernel module that actually does the work and performs the update. It verifies the [[PUP]] headers as well as the [[PUP#Packages|package]] headers then flashes the decrypted images directly to the [[eMMC]] with block writes.


= Update Package Decryption Code =
= Update Package Decryption Code =


The following code snippet will decrypt and update a directory of package files on 1.69 (the kernel functions called are specific to 1.69 NIDs). It is an attempted reproduction of the main code in <code>ScePsp2Swu</code>. The requirements are that you patch the application to be running in [[Vsh]] context (or specifically patch the [[AuthorityId|Authority ID]] to allow access to the kernel update functions). You also need an ability to read and write to the kernel from within your application. Finally, the updater will always attempt to flash the decrypted contents. If the flash failed because of model checks or whatever, it will return an error but the data will still be successfully decrypted and outputted. If it succeeds, note that you have now updated your PSVita.
The following code snippet will decrypt and update a directory of package files on FW 1.69 (the kernel functions called are specific to FW 1.69 prototypes). It is an attempted reproduction of the main code in <code>ScePsp2Swu</code>. The requirements are that you patch the application to be running in [[Vsh]] context (or specifically patch the [[AuthorityId|Authority ID]] to allow access to the kernel update functions). You also need an ability to read and write to the kernel from within your application. Finally, the updater will always attempt to flash the decrypted contents. If the flash failed because of model checks or whatever, it will return an error but the data will still be successfully decrypted and outputted. If it succeeds, note that you have now updated your PS Vita.


<source lang="c">
<source lang="C">
int start_decryption1(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
int update_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
     int argst[11];
     int args[11];
     int res;
     int res;
   
     memset(args, 0, 0x2C);
     memset(argst, 0, 0x2C);
     args[0] = 0x2C;
     argst[0]=0x2C;
     args[1] = code1;
     argst[1]=code1;
     args[2] = (int) buf;
     argst[2]=(int)buf;
     args[3] = buflen;
     argst[3]=buflen;
     args[4] = code2;
     argst[4]=code2;
     sceClibPrintf("Calling update_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x \n", package_type, (int)buf, buflen, code2);
     sceClibPrintf("Calling type 1 decryption with code1 = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x \n", code1, (int)buf, buflen, code2);
     res = sceSblUsUpdateSpackageForUser(package_type, args, pRequestId);
     res=callKernelFunction(sceSblUsUpdateSpackageForUser, code1, argst, phandle, 0);
     return res;
     return res;
}
}


int start_decryption2(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
int inspect_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
     int argst[11];
     int args[11];
     int res;
     int res;
   
     memset(args, 0, 0x2C);
     memset(argst, 0, 0x2C);
     args[0] = 0x2C;
     argst[0]=0x2C;
     args[1] = package_type;
     argst[1]=code1;
     args[2] = (int) buf;
     argst[2]=(int)buf;
     args[3] = buflen;
     argst[3]=buflen;
     args[4] = code2;
     argst[4]=code2;
     sceClibPrintf("Calling inspect_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", package_type, (int)buf, buflen, code2);
     sceClibPrintf("Calling type 2 decryption with code1 = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", code1, (int)buf, buflen, code2);
     res = sceSblUsInspectSpackageForUser(package_type, args, pRequestId);
     res=callKernelFunction(sceSblUsInspectSpackageForUser, code1, argst, phandle, 0);
     return res;
     return res;
}
}


int start_decryption3(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
int extract_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
     int argst[11];
     int args[11];
     int res;
     int res;
   
     memset(args, 0, 0x2C);
     memset(argst, 0, 0x2C);
     args[0] = 0x2C;
     argst[0]=0x2C;
     args[1] = package_type;
     argst[1]=code1;
     args[2] = (int) buf;
     argst[2]=(int)buf;
     args[3] = buflen;
     argst[3]=buflen;
     args[4] = code2;
     argst[4]=code2;
     sceClibPrintf("Calling extract_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", package_type, (int)buf, buflen, code2);
     sceClibPrintf("Calling type 3 decryption with code1 = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", code1, (int)buf, buflen, code2);
     res = sceSblUsExtractSpackageForUser(package_type, args, pRequestId);
     res=callKernelFunction(sceSblUsExtractSpackageForUser, code1, argst, phandle, 0);
     return res;
     return res;
}
}


int check_decryption_status(int code, int handle, int *out1, int *out2, int *out3, int *out4) {
int get_status(int node_type, int requestId, int *out1, int *out2, int *out3, int *out4) {
     int argst[11];
     int args[11];
     int res;  
     int res;
   
     memset(args, 0, 0x2C);
     memset(argst, 0, 0x2C);
     args[0] = 0x2C;
     argst[0]=0x2C;
     args[7] = (int) out1;
     argst[7]=(int)out1;
     args[8] = (int) out2;
     argst[8]=(int)out2;
     args[9] = (int) out3;
     argst[9]=(int)out3;
     args[10] = (int) out4;     
     argst[10]=(int)out4;     
     sceClibPrintf("Calling get_status with node_type = 0x%x requestId = 0x%x\n", node_type, requestId);
     sceClibPrintf("Calling status with code = 0x%x handle = 0x%x\n", code, handle);
     res = sceSblUsGetStatusForUser(node_type, requestId, args);
     res=callKernelFunction(sceSblUsGetStatusForUser, code, handle, argst, 0);
     return res;
     return res;
}
}


int get_final_size(unsigned char *buf) {
unsigned int get_final_size(unsigned char *buf) {
     int *poffs;
     int *poffs;
     int *psize;
     int *psize;
Line 709: Line 94:
}
}


int get_type(unsigned char *buf) {
int get_package_type(unsigned char *buf) {
     int *poffs;
     int *poffs;
     int *psize;
     int *psize;
     if ( *(int *)buf == 0x00454353 ) // "SCE\0"
     if (*(int *)buf == 0x00454353) { // "SCE\0"
    {
         poffs = (int *) (buf+0x10);
         poffs = (int *) (buf+0x10);
         psize = (int *) (buf+(*poffs)+4);
         psize = (int *) (buf+(*poffs)+4);
Line 719: Line 103:
     }
     }
     else
     else
    {
         return -1;
         return -1;
    }
}
}


Line 727: Line 109:
     int *poffs;
     int *poffs;
     poffs = (int *) (buf+0x10);
     poffs = (int *) (buf+0x10);
     return (buf+(*poffs)+0x80);
     return buf + *poffs + 0x80;
}
}


int complete_decryption(int code, int handle, unsigned char *buf, int maxlen) {
int get_extract_spackage(int node_type, int requestId, unsigned char *buf, int maxlen) {
     int argst[11];
     int args[11];
     int res;     
     int res;     
     unsigned char *payload;
     unsigned char *payload;
     int size;
     int size;
   
     memset(args, 0, 0x2C);
     memset(argst, 0, 0x2C);
     args[0] = 0x2C;
     argst[0]=0x2C;
     args[1] = node_type;
     argst[1]=code;
     args[5] = (int) buf;
     argst[5]=(int)buf;
     args[6] = maxlen;
     argst[6]=maxlen;
     sceClibPrintf("Calling get_extract_spackage with node_type = 0x%x requestId = 0x%x buf = 0x%x maxlen = 0x%x\n", node_type, requestId, (int)buf, maxlen);
     sceClibPrintf("Calling complete decryption with code = 0x%x handle = 0x%x buf = 0x%x maxlen = 0x%x\n", code, handle, (int)buf, maxlen);
     res = sceSblUsGetExtractSpackageForUser(node_type, requestId, args);
     res=callKernelFunction(sceSblUsGetExtractSpackageForUser, code, handle, argst, 0);
     return res;
     return res;
}
}
          
          
int do_decrypt_file(const char *inpath, const char *outpath, const char *errpath, unsigned int size)
int do_decrypt_spackage_file(const char *inpath, const char *outpath, const char *errpath, unsigned int us_buf_size) {
{
     int fd;
     int fd;
     int res;
     int res;
     int memid;
     int memid;
     int read;
     unsigned int read_size;
     int maxlen = 0x810000;
     unsigned int maxlen = 0x810000;
     int argst[0x2C/4];
     int args[0x2C/4];
     int id;
     int id;
     int type;
     int node_type;
     int code;
     int code;
     unsigned char *src, *outbuf;
     unsigned char *us_buf_addr, *outbuf;
     unsigned int handle, p1, p2, p3, p4;
     unsigned int requestId, p1, p2, p3, p4;
     res=callKernelFunction(sceSblUsAllocateBufferForUser, size, &src, 0, 0);
     res = sceSblUsAllocateBufferForUser(us_buf_size, &us_buf_addr);
     sceClibPrintf("Allocation returned 0x%x addr 0x%x\n", res, (int)src);
     sceClibPrintf("Allocation returned 0x%x addr 0x%x\n", res, (int)us_buf_addr);
     if(res) {
     if (res) {
         sceClibPrintf("Cannot allocate memory. (size 0x%x) fail.\n", size);
         sceClibPrintf("Cannot allocate buffer. (us_buf_size 0x%x) fail.\n", us_buf_size);
         return 0;
         return 0;
     }
     }
    //sceClibPrintf("Loading Firmware pkg file from host0:");
     fd = sceIoOpen(inpath, 1, 0);
     fd= sceIoOpen(inpath, 1, 0);
     read_size = 0;
     read = 0;
     while ((read_size = sceIoRead(fd, us_buf_addr, us_buf_size - read_size)) > 0);
     while ((read = sceIoRead(fd, src, size - read)) > 0);
     sceIoClose(fd);
     sceIoClose(fd);
     code = get_type(src);
     package_type = get_package_type(us_buf_addr);
     switch (code) {
     switch (package_type) {
         case -1:
         case -1:
             sceClibPrintf("Not an encrypted file.\n");
             sceClibPrintf("Not an encrypted file.\n");
Line 778: Line 157:
         case 4:
         case 4:
         case 0x1B:
         case 0x1B:
             type = 3;
             node_type = 3;
             res=start_decryption3(code, src, size, 9, &handle);
             res = extract_spackage(package_type, us_buf_addr, us_buf_size, 9, &requestId);
             break;
             break;
         case 0:
         case 0:
Line 788: Line 167:
         case 0xE:
         case 0xE:
         case 0x1A:
         case 0x1A:
             sceClibPrintf("Warning, code %x is unsupported!\n", code);
             sceClibPrintf("Warning, package_type %x is unsupported!\n", package_type);
         default:
         default:
             type = 2;
             node_type = 2;
             res=start_decryption2(code, src, size, 9, &handle);
             res = inspect_spackage(package_type, us_buf_addr, us_buf_size, 9, &requestId);
             break;
             break;
     }
     }
     if(res) {
     if (res) {
         sceClibPrintf("start_decryption failed. (0x%x)\n", res);
         sceClibPrintf("xxx_spackage failed. (0x%x)\n", res);
         goto ERROR;
         goto ERROR;
     }
     }
     for(;;) {
     for (;;) {
         res=check_decryption_status(type, handle, &p1, &p2, &p3, &p4);
         res = get_status(node_type, requestId, &p1, &p2, &p3, &p4);
         if(res) {
         if (res) {
             sceClibPrintf("check_decryption_status failed. (0x%x)\n", res);
             sceClibPrintf("get_status failed. (0x%x)\n", res);
             goto ERROR;
             goto ERROR;
         }
         }
         if(p3 == 5) {
         if (p3 == 5)
             break;
             break;
         } else {
         else
             sceKernelDelayThread(0x7A120);
             sceKernelDelayThread(500000);
        }
     }
     }
     sceClibPrintf("p1= 0x%x p2 = 0x%x p3 = 0x%x p4 = 0x%x\n", p1, p2, p3, p4);
     sceClibPrintf("p1= 0x%x p2 = 0x%x p3 = 0x%x p4 = 0x%x\n", p1, p2, p3, p4);
     if(p2 == 0) {
     if (p2 == 0) {
         sceClibPrintf("Starting to write %s\n", outpath);
         sceClibPrintf("Starting to write %s\n", outpath);
         fd = sceIoOpen(outpath, 0x603, 0x186);
         fd = sceIoOpen(outpath, 0x603, 0x186);
         read = get_final_size(src);
         read_size = get_final_size(us_buf_addr);
         while ((read -= sceIoWrite(fd, get_data_offset(src), read)) > 0);
         while ((read_size -= sceIoWrite(fd, get_data_offset(us_buf_addr), read_size)) > 0);
         sceIoClose(fd);
         sceIoClose(fd);
     } else {
     } else {
         sceClibPrintf("Error decrypting. Writing results to %s\n", errpath);
         sceClibPrintf("Error decrypting. Writing results to %s\n", errpath);
         fd= sceIoOpen(errpath, 0x603, 0x186);
         fd = sceIoOpen(errpath, 0x603, 0x186);
         read = get_final_size(src);
         read_size = get_final_size(us_buf_addr);
         while ((read -= sceIoWrite(fd, get_data_offset(src), read)) > 0);
         while ((read_size -= sceIoWrite(fd, get_data_offset(us_buf_addr), read_size)) > 0);
         sceIoClose(fd);
         sceIoClose(fd);
         goto ERROR;
         goto ERROR;
     }
     }
 
     res = get_extract_spackage(node_type, requestId, us_buf_addr, maxlen);
     res=complete_decryption(type, handle, src, maxlen);
     if (res) {
     if(res) {
         sceClibPrintf("get_extract_spackage failed. (0x%x)\n", res);
         sceClibPrintf("complete_decryption failed. (0x%x)\n", res);
         goto ERROR;
         goto ERROR;
     }
     }
 
     res = sceSblUsReleaseBufferForUser(us_buf_addr);
     res=callKernelFunction(sceSblUsReleaseBufferForUser, src, 0, 0, 0);
     return 1;
     return 1;
ERROR:
ERROR:
     res=callKernelFunction(sceSblUsReleaseBufferForUser, src, 0, 0, 0);
     res = sceSblUsReleaseBufferForUser(us_buf_addr);
     return 0;
     return 0;
}
}


void do_decrypt_dir(const char *path)
void do_decrypt_spackage_dir(const char *path) {
{
     int fd;
     int fd;
     SceIoDirent dir;
     SceIoDirent dir;
Line 846: Line 221:
     char output[256];
     char output[256];
     char errput[256];
     char errput[256];
 
     if ((fd = sceIoDopen(path)) < 0) {
     if ((fd = sceIoDopen(path)) < 0)
         sceClibPrintf("Error opening spackage directory.\n");
    {
         sceClibPrintf("Error opening pkg dir.\n");
         return;
         return;
     }
     }
 
     while (sceIoDread(fd, &dir) > 0) {
     while (sceIoDread(fd, &dir) > 0)
    {
         sprintf(input, "%s/%s", path, dir.d_name);
         sprintf(input, "%s/%s", path, dir.d_name);
         sprintf(output, "%s/%s.dec", path, dir.d_name);
         sprintf(output, "%s/%s.dec", path, dir.d_name);
         sprintf(errput, "%s/%s.err", path, dir.d_name);
         sprintf(errput, "%s/%s.err", path, dir.d_name);
         sceClibPrintf("Decrypting %s (size 0x%x)\n", input, (unsigned int)dir.d_stat.st_size);
         sceClibPrintf("Decrypting %s (size 0x%x)\n", input, (unsigned int)dir.d_stat.st_size);
         if (do_decrypt_file(input, output, errput, (unsigned int)dir.d_stat.st_size))
         if (do_decrypt_spackage_file(input, output, errput, (unsigned int)dir.d_stat.st_size))
             sceClibPrintf("Decrypted to %s\n", output);
             sceClibPrintf("Decrypted to %s\n", output);
         else
         else
             sceClibPrintf("Failed to decrypt %s\n", dir.d_name);
             sceClibPrintf("Failed to decrypt %s\n", dir.d_name);
     }
     }
     sceIoDclose(fd);
     sceIoDclose(fd);
}
}
Line 871: Line 241:
= Bootloader =
= Bootloader =


The SLSK bootloaders are re-encrypted with per-console keys during the update process. <code>second_loader.enp</code> and <code>second_loader.enc</code> are transformed into <code>second_loader.enp_</code> and <code>second_loader.enp</code> respectively by F00D before flashing to eMMC (and the same thing is done to <code>secure_kernel</code>).
The SLSK bootloaders are re-encrypted with per-console keys during the update process. <code>second_loader.enp</code> and <code>second_loader.enc</code> are transformed into <code>second_loader.enp_</code> and <code>second_loader.enp</code> respectively by cMeP before flashing to eMMC (and the same thing is done to <code>secure_kernel</code>).


= Some useful notes =
= Some useful notes =


[[#sceSblUsUpdateSpackageForUser|sceSblUsUpdateSpackageForUser]] does the bulk of the work decrypting and flashing all the update parts. There appears to be two ways to skip the version checks (but not revokion checks).
[[SceSblUpdateMgr#sceSblUsUpdateSpackageForUser|sceSblUsUpdateSpackageForUser]] does the bulk of the work decrypting and flashing all the update parts. There appears to be two ways to skip the version checks (but not revokion checks).


<s>First way is to patch the imported function <code>SceQafMgrForDriver_8C423C18(void);</code> to return 1. Alternatively, patch [[Sysroot]] offset 0x2C+3 and set bit 0x2. This will bypass ALL version checks, including the peripherals which might be dangerous.</s>
<s>First way is to patch the imported function <code>SceQafMgrForDriver_8C423C18(void);</code> to return 1. Alternatively, patch [[KBL Param]] offset 0x2C+3 and set bit 0x2. This will bypass ALL version checks, including the peripherals which might be dangerous.</s>


Update: don't do this, it does brick as expected.
Update: do not do this. It does brick as expected.


<s>Second way is to patch <code>SceVshBridge</code> export of <code>vshSblAimgrIsCEX</code> (takes no arguments) to return 0. Alternatively patch <code>ScePsp2Swu</code>'s import of that function.</s>
<s>Second way is to patch <code>SceVshBridge</code> export of <code>vshSblAimgrIsCEX</code> (takes no arguments) to return 0. Alternatively patch <code>ScePsp2Swu</code>'s import of that function.</s>


Update: this doesn't work because the export is used by other functions and fails earlier checks. This flag is set by psp2swu.self to indicate bypassing of version checks on the bootloader and system partitions. All other components will be updated if at higher version. This is what devkits do by default. You can also patch the flags directly. In <code>sceSblUsUpdateSpackageForUser</code>, <code>sceSblUsInspectSpackageForUser</code>, and <code>sceSblUsExtractSpackageForUser</code> (in order: flash, dry run, decrypt only) you can patch the flags argument directly and set <code>0x8</code> to indicate skipping version check on bootloader and system partitions. The flags argument found in R1 (second argument) as a user memory pointer offset 0x10.
Update: this does not work because the export is used by other functions and fails earlier checks. This flag is set by psp2swu.self to indicate bypassing of version checks on the bootloader and system partitions. All other components will be updated if at higher version. This is what DevKits do by default. You can also patch the flags directly. In <code>sceSblUsUpdateSpackageForUser</code>, <code>sceSblUsInspectSpackageForUser</code>, and <code>sceSblUsExtractSpackageForUser</code> (in order: flash, dry run, decrypt only) you can patch the flags argument directly and set <code>0x8</code> to indicate skipping version check on bootloader and system partitions. The flags argument found in R1 (second argument) as a user memory pointer offset 0x10.
 
Authority ID for psp2swu.self is either <code>2800800000000002</code> or <code>2800800000000003</code>.


Auth id for psp2swu.self is either <code>2800800000000002</code> or <code>2800800000000003</code>.
Minimal FW update version for a PS Vita is checked at two layers at least:
* From Idstorage SMI leaf. Two AES encryption layers and one RSA. Highest minimal FW version is 3.65.
* From UpdateMgr embedded list. Constant and depending on hardware. Highest minimal FW version is 3.50.




[[Category:Applications]]
[[Category:Applications]]
[[Category:Kernel]]
[[Category:Kernel]]

Latest revision as of 06:42, 22 January 2023

Various components in user, kernel, and secure kernel work together to update the system. All the relevant libraries are documented in this one page because they work close together.

Update Process

The PS Vita updater is composed of various parts:

  • SceCuiSetUpper
  • ScePsp2Swu
  • SceSblSsUpdateMgr
  • update_service_sm

Initiator

The first part is the initiator of the update. The responsibility of the initiator is to copy the update file (PSP2UPDAT.PUP) to the ud0 partition and extract psp2swu.self from the update file to ud0:PSP2UPDATE/psp2swu.self. Then it signals the syscon to start in update mode, where psp2swu.self is loaded instead of the usual shell.

The initiator that most people use is likely the update option in SceSettings. Another is the update option in SceSafeMode. Another option is SceCuiSetUpper, which is extracted from the update PUP. SceCuiSetUpper is likely used in development units as it can load the update data from host0, which only exists on development units. This file, however, can be found in retail update packages. SceCuiSetUpper also sets a flag to start updater in CUI mode.

ScePsp2Swu

Once the system restarts into update mode, the updater runs. Normally, it runs in GUI mode which is the green screen with the progress bar. If a flag is set by the initiator, the updater will run in CUI mode, which is a text interface that provides more verbose information about the update process.

The updater first makes calls to SceSblSsUpdateMgr to verify the PUP header. For more information, check out PUP. Next, the updater will spawn a thread that calls into the kernel to decrypt, verify, and flash each package file according to the information found in the package header. For more information, check out PUP#Packages.

SceSblSsUpdateMgr

This is a kernel module that actually does the work and performs the update. It verifies the PUP headers as well as the package headers then flashes the decrypted images directly to the eMMC with block writes.

Update Package Decryption Code

The following code snippet will decrypt and update a directory of package files on FW 1.69 (the kernel functions called are specific to FW 1.69 prototypes). It is an attempted reproduction of the main code in ScePsp2Swu. The requirements are that you patch the application to be running in Vsh context (or specifically patch the Authority ID to allow access to the kernel update functions). You also need an ability to read and write to the kernel from within your application. Finally, the updater will always attempt to flash the decrypted contents. If the flash failed because of model checks or whatever, it will return an error but the data will still be successfully decrypted and outputted. If it succeeds, note that you have now updated your PS Vita.

int update_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
    int args[11];
    int res;
    memset(args, 0, 0x2C);
    args[0] = 0x2C;
    args[1] = code1;
    args[2] = (int) buf;
    args[3] = buflen;
    args[4] = code2;
    sceClibPrintf("Calling update_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x \n", package_type, (int)buf, buflen, code2);
    res = sceSblUsUpdateSpackageForUser(package_type, args, pRequestId);
    return res;
}

int inspect_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
    int args[11];
    int res;
    memset(args, 0, 0x2C);
    args[0] = 0x2C;
    args[1] = package_type;
    args[2] = (int) buf;
    args[3] = buflen;
    args[4] = code2;
    sceClibPrintf("Calling inspect_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", package_type, (int)buf, buflen, code2);
    res = sceSblUsInspectSpackageForUser(package_type, args, pRequestId);
    return res;
}

int extract_spackage(int package_type, unsigned char *buf, int buflen, int code2, int *pRequestId) {
    int args[11];
    int res;
    memset(args, 0, 0x2C);
    args[0] = 0x2C;
    args[1] = package_type;
    args[2] = (int) buf;
    args[3] = buflen;
    args[4] = code2;
    sceClibPrintf("Calling extract_spackage with package_type = 0x%x buf = 0x%x buflen = 0x%x code2 = 0x%x\n", package_type, (int)buf, buflen, code2);
    res = sceSblUsExtractSpackageForUser(package_type, args, pRequestId);
    return res;
}

int get_status(int node_type, int requestId, int *out1, int *out2, int *out3, int *out4) {
    int args[11];
    int res;
    memset(args, 0, 0x2C);
    args[0] = 0x2C;
    args[7] = (int) out1;
    args[8] = (int) out2;
    args[9] = (int) out3;
    args[10] = (int) out4;    
    sceClibPrintf("Calling get_status with node_type = 0x%x requestId = 0x%x\n", node_type, requestId);
    res = sceSblUsGetStatusForUser(node_type, requestId, args);
    return res;
}

unsigned int get_final_size(unsigned char *buf) {
    int *poffs;
    int *psize;
    poffs = (int *) (buf+0x10);
    psize = (int *) (buf+(*poffs)+0x20);
    return *psize;
}

int get_package_type(unsigned char *buf) {
    int *poffs;
    int *psize;
    if (*(int *)buf == 0x00454353) { // "SCE\0"
        poffs = (int *) (buf+0x10);
        psize = (int *) (buf+(*poffs)+4);
        return *psize;
    }
    else
        return -1;
}

unsigned char *get_data_offset(unsigned char *buf) {
    int *poffs;
    poffs = (int *) (buf+0x10);
    return buf + *poffs + 0x80;
}

int get_extract_spackage(int node_type, int requestId, unsigned char *buf, int maxlen) {
    int args[11];
    int res;    
    unsigned char *payload;
    int size;
    memset(args, 0, 0x2C);
    args[0] = 0x2C;
    args[1] = node_type;
    args[5] = (int) buf;
    args[6] = maxlen;
    sceClibPrintf("Calling get_extract_spackage with node_type = 0x%x requestId = 0x%x buf = 0x%x maxlen = 0x%x\n", node_type, requestId, (int)buf, maxlen);
    res = sceSblUsGetExtractSpackageForUser(node_type, requestId, args);
    return res;
}
        
int do_decrypt_spackage_file(const char *inpath, const char *outpath, const char *errpath, unsigned int us_buf_size) {
    int fd;
    int res;
    int memid;
    unsigned int read_size;
    unsigned int maxlen = 0x810000;
    int args[0x2C/4];
    int id;
    int node_type;
    int code;
    unsigned char *us_buf_addr, *outbuf;
    unsigned int requestId, p1, p2, p3, p4;
    res = sceSblUsAllocateBufferForUser(us_buf_size, &us_buf_addr);
    sceClibPrintf("Allocation returned 0x%x addr 0x%x\n", res, (int)us_buf_addr);
    if (res) {
        sceClibPrintf("Cannot allocate buffer. (us_buf_size 0x%x) fail.\n", us_buf_size);
        return 0;
    }
    fd = sceIoOpen(inpath, 1, 0);
    read_size = 0;
    while ((read_size = sceIoRead(fd, us_buf_addr, us_buf_size - read_size)) > 0);
    sceIoClose(fd);
    package_type = get_package_type(us_buf_addr);
    switch (package_type) {
        case -1:
            sceClibPrintf("Not an encrypted file.\n");
            goto ERROR;
        case 3:
        case 4:
        case 0x1B:
            node_type = 3;
            res = extract_spackage(package_type, us_buf_addr, us_buf_size, 9, &requestId);
            break;
        case 0:
        case 2:
        case 5:
        case 6:
        case 7:
        case 0xE:
        case 0x1A:
            sceClibPrintf("Warning, package_type %x is unsupported!\n", package_type);
        default:
            node_type = 2;
            res = inspect_spackage(package_type, us_buf_addr, us_buf_size, 9, &requestId);
            break;
    }
    if (res) {
        sceClibPrintf("xxx_spackage failed. (0x%x)\n", res);
        goto ERROR;
    }
    for (;;) {
        res = get_status(node_type, requestId, &p1, &p2, &p3, &p4);
        if (res) {
            sceClibPrintf("get_status failed. (0x%x)\n", res);
            goto ERROR;
        }
        if (p3 == 5)
            break;
        else
            sceKernelDelayThread(500000);
    }
    sceClibPrintf("p1= 0x%x p2 = 0x%x p3 = 0x%x p4 = 0x%x\n", p1, p2, p3, p4);
    if (p2 == 0) {
        sceClibPrintf("Starting to write %s\n", outpath);
        fd = sceIoOpen(outpath, 0x603, 0x186);
        read_size = get_final_size(us_buf_addr);
        while ((read_size -= sceIoWrite(fd, get_data_offset(us_buf_addr), read_size)) > 0);
        sceIoClose(fd);
    } else {
        sceClibPrintf("Error decrypting. Writing results to %s\n", errpath);
        fd = sceIoOpen(errpath, 0x603, 0x186);
        read_size = get_final_size(us_buf_addr);
        while ((read_size -= sceIoWrite(fd, get_data_offset(us_buf_addr), read_size)) > 0);
        sceIoClose(fd);
        goto ERROR;
    }
    res = get_extract_spackage(node_type, requestId, us_buf_addr, maxlen);
    if (res) {
        sceClibPrintf("get_extract_spackage failed. (0x%x)\n", res);
        goto ERROR;
    }
    res = sceSblUsReleaseBufferForUser(us_buf_addr);
    return 1;
ERROR:
    res = sceSblUsReleaseBufferForUser(us_buf_addr);
    return 0;
}

void do_decrypt_spackage_dir(const char *path) {
    int fd;
    SceIoDirent dir;
    char input[256];
    char output[256];
    char errput[256];
    if ((fd = sceIoDopen(path)) < 0) {
        sceClibPrintf("Error opening spackage directory.\n");
        return;
    }
    while (sceIoDread(fd, &dir) > 0) {
        sprintf(input, "%s/%s", path, dir.d_name);
        sprintf(output, "%s/%s.dec", path, dir.d_name);
        sprintf(errput, "%s/%s.err", path, dir.d_name);
        sceClibPrintf("Decrypting %s (size 0x%x)\n", input, (unsigned int)dir.d_stat.st_size);
        if (do_decrypt_spackage_file(input, output, errput, (unsigned int)dir.d_stat.st_size))
            sceClibPrintf("Decrypted to %s\n", output);
        else
            sceClibPrintf("Failed to decrypt %s\n", dir.d_name);
    }
    sceIoDclose(fd);
}

Bootloader

The SLSK bootloaders are re-encrypted with per-console keys during the update process. second_loader.enp and second_loader.enc are transformed into second_loader.enp_ and second_loader.enp respectively by cMeP before flashing to eMMC (and the same thing is done to secure_kernel).

Some useful notes

sceSblUsUpdateSpackageForUser does the bulk of the work decrypting and flashing all the update parts. There appears to be two ways to skip the version checks (but not revokion checks).

First way is to patch the imported function SceQafMgrForDriver_8C423C18(void); to return 1. Alternatively, patch KBL Param offset 0x2C+3 and set bit 0x2. This will bypass ALL version checks, including the peripherals which might be dangerous.

Update: do not do this. It does brick as expected.

Second way is to patch SceVshBridge export of vshSblAimgrIsCEX (takes no arguments) to return 0. Alternatively patch ScePsp2Swu's import of that function.

Update: this does not work because the export is used by other functions and fails earlier checks. This flag is set by psp2swu.self to indicate bypassing of version checks on the bootloader and system partitions. All other components will be updated if at higher version. This is what DevKits do by default. You can also patch the flags directly. In sceSblUsUpdateSpackageForUser, sceSblUsInspectSpackageForUser, and sceSblUsExtractSpackageForUser (in order: flash, dry run, decrypt only) you can patch the flags argument directly and set 0x8 to indicate skipping version check on bootloader and system partitions. The flags argument found in R1 (second argument) as a user memory pointer offset 0x10.

Authority ID for psp2swu.self is either 2800800000000002 or 2800800000000003.

Minimal FW update version for a PS Vita is checked at two layers at least:

  • From Idstorage SMI leaf. Two AES encryption layers and one RSA. Highest minimal FW version is 3.65.
  • From UpdateMgr embedded list. Constant and depending on hardware. Highest minimal FW version is 3.50.