Updater

From Vita Development Wiki
Jump to navigation Jump to search

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

Module

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.

Known NIDs

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

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 bootfs:update_mgr.skprx.

Known NIDs

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

Version Name World Visibility NID
1.69 SceSblUpdateMgrForKernel Non-secure Kernel 0xC4466E48
1.69 SceSblUpdateMgrForDriver Non-secure Kernel 0x0E04CD3D
1.69-3.60 SceSblSsUpdateMgr Non-secure User 0x31406C49
0.990 SceSblSsUpdateMgrAdditional Non-secure User 0xBDD7A86F

Types

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;

SceSblUpdateMgrForDriver

sceSblUsVerifyPupForDriver

Version NID
0.940 0xD593D613

sceSblUsVerifyPupHeaderForDriver

Version NID
0.940 0xBAFCA304

sceSblUsVerifyPupSegmentForDriver

Version NID
0.931-0.940 0xF43372C4
int sceSblUsVerifyPupSegmentForDriver(const char *path, int a2, int a3, int a4);

sceSblUsVerifyPupSegmentByIdForDriver

Version NID
0.940 0xB4AC7684

sceSblUsVerifyPupWatermarkForDriver

Version NID
0.940 0xDD90C4B9

sceSblUpdateSpackageForDriver

Version NID
0.931-0.990 0xF41138F1

Temp name was sceSblUsUpdateSpackageForDriver.

int sceSblUpdateSpackageForDriver(uint32_t type, void *kaddr, SceSize size, uint32_t flags, int *pRequestId);

sceSblUsInspectSpackageForDriver

Version NID
0.990 0xE7F5A4C0

sceSblUsExtractSpackageForDriver

Version NID
0.990 0x87AC6E73

sceSblUsAllocateBufferForDriver

Version NID
0.990 0x2D69BFDC

sceSblUsReleaseBufferForDriver

Version NID
0.990 0x45B91736

sceSblUsGetStatusForDriver

Version NID
0.990 0x99D57D18

sceSblUsGetSpkgInfoForDriver

Version NID
0.931-0.990 0xAE7D3BF5
int sceSblUsGetSpkgInfoForDriver(int type, SceSblUsSpkgInfo *pInfo);

sceSblUsGetUpdateModeForDriver

Version NID
0.990 0xEEC71CCC

sceSblUsSetUpdateModeForDriver

Version NID
0.990 0x266820E9

SceSblUpdateMgrForDriver_return_0

Version NID
0.990 0x6ACEF44D

sceSblUsPowerControlForDriver

Version NID
0.931-0.990 0x64ECC81A
int sceSblUsPowerControlForDriver(int a1, int a2);

sceSblUsGetApplicableVersionForDriver

Version NID
0.931-0.990 0x7CC73839
// type must be 1 or 9 else "builtin revoke list cannot be found"
int sceSblUsGetApplicableVersionForDriver(int type, void *versionBuf);

SceSblSsUpdateMgrAdditional

sceSblUsInformUpdateStartedForUser

Version NID
0.990 0x1E40A14E

sceSblUsInformUpdateOngoingForUser

Version NID
0.990 0x3A917CCE

sceSblUsInformUpdateFinishedForUser

Version NID
0.990 0x4734B987

sceSblUsSetSwInfoIntForUser

Version NID
0.990 0xA870D285

sceSblUsSetSwInfoStrForUser

Version NID
0.990 0x8C7255C8

sceSblUsSetSwInfoBinForUser

Version NID
0.990 0xF157E34A

SceSblSsUpdateMgr

sceSblUsGetUpdateModeForUser

Version NID
0.931-3.60 0x8E834565

Temp name was sceSblSsUpdateMgrGetBootMode.

Get UpdateMode from Ernie NVS. See SceSblSsMgr#NVS_Areas.

int sceSblUsGetUpdateModeForUser(SceUpdateMode *mode);

sceSblUsSetUpdateModeForUser

Version NID
0.990-3.60 0xC725E3F0

Temp name was sceSblSsUpdateMgrSetBootMode.

int sceSblUsSetUpdateModeForUser(SceUpdateMode mode);

sceSblUsPowerControlForUser

Version NID
0.990-3.60 0x1825D954

Temp name was sceSblSsUpdateMgrSendCommand.

int sceSblUsPowerControlForUser(int arg1, int arg2);

sceSblUsGetSpkgInfoForUser

Version NID
0.990-3.60 0x8E3EC2E1

Temp name was sceSblSsUpdateMgrGetSpkgInfo.

int sceSblUsGetSpkgInfoForUser(int type, SceSblUsSpkgInfo *pInfo);

sceSblUsVerifyPupForUser

Version NID
0.940-3.60 0x6F5EDBF4

path max len : 0x3FF, path len >= 0x400 : error

int sceSblUsVerifyPupForUser(const char *path);

sceSblUsVerifyPupAdditionalSignForUser

Version NID
0.990 NOT PRESENT
3.60 0xB19366CB

path max len : 0x3FF, path len >= 0x400 : error

int sceSblUsVerifyPupAdditionalSignForUser(const char *path);

sceSblUsVerifyPupHeaderForUser

Version NID
0.940-3.60 0x9BE17A06

path max len : 0x3FF, path len >= 0x400 : error

int sceSblUsVerifyPupHeaderForUser(const char *path);

sceSblUsVerifyPupSegmentForUser

Version NID
0.931-3.60 0xD47FD33E

path max len: 0x3FF bytes

int sceSblUsVerifyPupSegmentForUser(const char *path);

sceSblUsVerifyPupSegmentByIdForUser

Version NID
0.940-3.60 0x95FC1A0A

path max len: 0x3FF bytes

Since arg2 is internally rewritten to 0x4000001, it is set to 0.

Maybe that seg_id is uint64_t and so it might be part of arg2 or arg4

int sceSblUsVerifyPupSegmentByIdForUser(const char *path, void *arg2, int seg_id, int arg4);

sceSblUsVerifyPupWatermarkForUser

Version NID
0.940-3.60 0xC6CDEB8D

path max len : 0x3FF, path len >= 0x400 : error

int sceSblUsVerifyPupWatermarkForUser(const char *path);

sceSblUsUpdateSpackageForUser

Version NID
0.990-3.60 0x6E8DDAC4
typedef struct SceUpdateSpackageArgs { // Size is 0x2C on FW 0.931
  SceSize arg_size; // Size of this structure
  SceUInt32 type;
  void *addr;
  SceSize size;
  SceUInt32 flags;
  char reserved[0x18];
} SceUpdateSpackageArgs;

int sceSblUsUpdateSpackageForUser(int type, SceUpdateSpackageArgs *_args, int *pRequestId);

sceSblUsInspectSpackageForUser

Version NID
0.990-3.60 0x1A39F6EE
int sceSblUsInspectSpackageForUser(int arg1, void *arg2, void *arg3);

sceSblUsExtractSpackageForUser

Version NID
0.990-3.60 0xC1792A1C
int sceSblUsExtractSpackageForUser(int arg1, void *arg2, void *arg3);

sceSblUsGetExtractSpackageForUser

Version NID
0.990-3.60 0x4897AD56
int sceSblUsGetExtractSpackageForUser(int index, int request_id, SceUpdateSpackageArgs *_args);

sceSblUsAllocateBufferForUser

Version NID
0.931-3.60 0x4C06F41C
int sceSblUsAllocateBufferForUser(SceSize, void **uaddr);

sceSblUsReleaseBufferForUser

Version NID
0.990-3.60 0xBD677F5A
int sceSblUsReleaseBufferForUser(int arg1);

sceSblUsGetStatusForUser

Version NID
0.990-3.60 0xF403143E
int sceSblUsGetStatusForUser(int arg1, int arg2, void *arg3);

sceSblUsCheckSystemIntegrityForUser

Version NID
0.990-3.60 0xBED8DFC7

Do nothing

int SceSblSsUpdateMgr_BED8DFC7(void)
{
	int state;

	if (ksceSblACIsSystemProgram(0) == 0)
		return 0x800F022C;

	ENTER_SYSCALL(state);
	EXIT_SYSCALL(state);

	return 0;
}
int sceSblUsCheckSystemIntegrityForUser(void);

sceSblUsGetApplicableVersionForUser

Version NID
0.931-3.60 0x3ADD4B7A
// type must be 1 or 9 else "builtin revoke list cannot be found"
int sceSblUsGetApplicableVersionForUser(int type, void *versionBuf);

SceSblSsUpdateMgr_9FC8E905

Version NID
0.990 NOT PRESENT
3.60 0x9FC8E905
int SceSblSsUpdateMgr_9FC8E905(int arg1, int arg2, const void *src, int len);

SceSblSsUpdateMgr_D0CB50AC

Version NID
0.990 NOT PRESENT
3.60 0xD0CB50AC
int SceSblSsUpdateMgr_D0CB50AC(int arg1, int arg2);

SceSblSsUpdateMgr_2A02DCFB

Version NID
0.990 NOT PRESENT
3.60 0x2A02DCFB
int SceSblSsUpdateMgr_2A02DCFB(int arg1, const void *src, int len);

SceSblSsUpdateMgr_157AD4AD

Version NID
0.990 NOT PRESENT
3.60 0x157AD4AD
int SceSblSsUpdateMgr_157AD4AD(const void *src, int len, int arg3);

SceSblSsUpdateMgr_FE930747

Version NID
0.990 NOT PRESENT
3.60 0xFE930747
int SceSblSsUpdateMgr_FE930747(const void *src1, int len1, const void *src2, int len2);

SceSblSsUpdateMgr_92A8002B

Version NID
0.990 NOT PRESENT
3.60 0x92A8002B
int SceSblSsUpdateMgr_92A8002B(const void *src1, int len1, const void *src2, int len2);

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 (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 and 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 1.69 (the kernel functions called are specific to 1.69 NIDs). 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 PSVita.

int start_decryption1(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
    int argst[11];
    int res;
    
    memset(argst, 0, 0x2C);
    argst[0]=0x2C;
    argst[1]=code1;
    argst[2]=(int)buf;
    argst[3]=buflen;
    argst[4]=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=callKernelFunction(sceSblUsUpdateSpackageForUser, code1, argst, phandle, 0);
    return res;
}

int start_decryption2(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
    int argst[11];
    int res;
    
    memset(argst, 0, 0x2C);
    argst[0]=0x2C;
    argst[1]=code1;
    argst[2]=(int)buf;
    argst[3]=buflen;
    argst[4]=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=callKernelFunction(sceSblUsInspectSpackageForUser, code1, argst, phandle, 0);
    return res;
}

int start_decryption3(int code1, unsigned char *buf, int buflen, int code2, int *phandle) {
    int argst[11];
    int res;
    
    memset(argst, 0, 0x2C);
    argst[0]=0x2C;
    argst[1]=code1;
    argst[2]=(int)buf;
    argst[3]=buflen;
    argst[4]=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=callKernelFunction(sceSblUsExtractSpackageForUser, code1, argst, phandle, 0);
    return res;
}

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

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

int get_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 complete_decryption(int code, int handle, unsigned char *buf, int maxlen) {
    int argst[11];
    int res;    
    unsigned char *payload;
    int size;
    
    memset(argst, 0, 0x2C);
    argst[0]=0x2C;
    argst[1]=code;
    argst[5]=(int)buf;
    argst[6]=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=callKernelFunction(sceSblUsGetExtractSpackageForUser, code, handle, argst, 0);
    return res;
}
        
int do_decrypt_file(const char *inpath, const char *outpath, const char *errpath, unsigned int size)
{
    int fd;
    int res;
    int memid;
    int read;
    int maxlen = 0x810000;
    int argst[0x2C/4];
    int id;
    int type;
    int code;
    unsigned char *src, *outbuf;
    unsigned int handle, p1, p2, p3, p4;
    res=callKernelFunction(sceSblUsAllocateBufferForUser, size, &src, 0, 0);
    sceClibPrintf("Allocation returned 0x%x addr 0x%x\n", res, (int)src);
    if(res) {
        sceClibPrintf("Cannot allocate memory. (size 0x%x) fail.\n", size);
        return 0;
    }
    //sceClibPrintf("Loading Firmware pkg file from host0:");
    fd= sceIoOpen(inpath, 1, 0);
    read = 0;
    while ((read = sceIoRead(fd, src, size - read)) > 0);
    sceIoClose(fd);
    code = get_type(src);
    switch (code) {
        case -1:
            sceClibPrintf("Not an encrypted file.\n");
            goto ERROR;
        case 3:
        case 4:
        case 0x1B:
            type = 3;
            res=start_decryption3(code, src, size, 9, &handle);
            break;
        case 0:
        case 2:
        case 5:
        case 6:
        case 7:
        case 0xE:
        case 0x1A:
            sceClibPrintf("Warning, code %x is unsupported!\n", code);
        default:
            type = 2;
            res=start_decryption2(code, src, size, 9, &handle);
            break;
    }
    if(res) {
        sceClibPrintf("start_decryption failed. (0x%x)\n", res);
        goto ERROR;
    }
    for(;;) {
        res=check_decryption_status(type, handle, &p1, &p2, &p3, &p4);
        if(res) {
            sceClibPrintf("check_decryption_status failed. (0x%x)\n", res);
            goto ERROR;
        }
        if(p3 == 5) {
            break;
        } else { 
            sceKernelDelayThread(0x7A120);
        } 
    }
    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 = get_final_size(src);
        while ((read -= sceIoWrite(fd, get_data_offset(src), read)) > 0);
        sceIoClose(fd);
    } else {
        sceClibPrintf("Error decrypting. Writing results to %s\n", errpath);
        fd= sceIoOpen(errpath, 0x603, 0x186);
        read = get_final_size(src);
        while ((read -= sceIoWrite(fd, get_data_offset(src), read)) > 0);
        sceIoClose(fd);
        goto ERROR;
    }

    res=complete_decryption(type, handle, src, maxlen);
    if(res) {
        sceClibPrintf("complete_decryption failed. (0x%x)\n", res);
        goto ERROR;
    }

    res=callKernelFunction(sceSblUsReleaseBufferForUser, src, 0, 0, 0);
    return 1;
ERROR:
    res=callKernelFunction(sceSblUsReleaseBufferForUser, src, 0, 0, 0);
    return 0;
}

void do_decrypt_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 pkg dir.\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_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

There's evidence that the bootloaders are re-encrypted with probably 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 F00D before flashing (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 Sysroot offset 0x2C+3 and set bit 0x2. This will bypass ALL version checks, including the peripherals which might be dangerous.

Update: don't 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 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 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.

Auth id for psp2swu.self is either 2800800000000002 or 2800800000000003.