Second Loader

From Vita Development Wiki
Jump to navigation Jump to search

FW Difference

FW version What changed
3.51 unknown
3.52 FW strings/inst binary, Build date, bootloader revision.

Notes

If the Ernie version is less than 0x80300, second_loader refuses to boot.

Misc function

sceKernelCheckDipsw

Version Offset
3.60 0x802088

sceSysconReadScratchPad

Version Offset
3.60 0x804530

sceSysconCtrlSdPower

Version Offset
3.60 0x8048a6

SceSysconForDriver_7F198FA2

Version Offset
3.60 0x8048b0

Boot type indicator 1 for SLSK

This flag is set to cmep keyring 0x50C. It is defined at same time as KBL Param#Boot type indicator 1 and embeds some common information but with different flags.

cmep keyring 0x50C KBL Param Boot type indicator 1
0x4 0x1
0x20 0x10000
0x100 0x20000
0x8 no equivalent

Boot process (3.60)

TODO

int __usercall boot_800432@<W4>()
{
  v51 = v0;
  setup_gpio_0_1_leds_809422();
  gpio_something_809582(0LL, 7LL, 0LL);
  gpio_something2_80950A(0LL, 7LL);
  memset_FF_16_bytes_802302();
  sub_80193E();
  clear_line_0x516_80255C();

EMMC and syscon init

  set_emmc_hook_80712A(0x80374ELL);             // syscon_setup_80374E
  ret = try_init_emmc_801070();
  v50 = ret;
  if ( ret )
    goto LABEL_97;
  get_syscon_setup_cached_retval_807132((__int64)&v50);
  ret = v50;
  if ( v50 )
    goto LABEL_97;

emmc device (sdif0) is initialized. It sets keys for emmc crypto to be 0x20E/0x20F pair and protects them (dword_E0030024 = 0x1FEF020F; dword_E0030024 = 0x1FEF020E; unk_E0070008 = 0x20E020F; unk_E0070000 = 0; unk_E0070000 = 1;)

The command sequence sent to emmc is GO_IDLE_STATE, SEND_OP_COND, SEND_CID, SET_RELATIVE_ADDR, SEND_CSD, SELECT_CARD, SEND_EXT_CSD, SET_BLOCKLEN, SWITCH, SWITCH.

During SEND_OP_COND the function pointer from set_emmc_hook is called, it performs syscon init. It enables syscon device using gpio and sends syscon commands 1 (get syscon ver), 5 (get vita model info), 2 (get syscon date string), optionally 0x80.

Model/devkit checks

  model_info = get_cached_model_info_8038F2();
  model_info_1 = model_info;
  set_global_model_info_808C24(model_info);
  model_upper = model_info_1 & 0xFF0000;
  if ( model_upper == 0x600000 )
    goto its_devkit;
  if ( model_upper > 0x600000 )
    goto LABEL_8;
  if ( model_upper == 0x310000 )
    goto its_devkit;
  v5 = 0x100000;
  if ( model_upper > 0x310000 )
  {
    if ( model_upper == 0x410000 )
      goto its_devkit;
LABEL_8:
    if ( model_upper == 0x800000 || model_upper <= 0x800000 && model_upper == 0x700000 || model_upper == 0x820000 )
      goto its_devkit;
    v5 = 0x900000;
    goto LABEL_13;
  }
  do
  {
LABEL_13:
    if ( model_upper == v5 )
    {
its_devkit:
      sub_802044();
      enable_leds_808B7A();
    }
    set_status(65LL);
    v6 = get_syscon_ver_8038EC();
    v5 = 0x802FF;
    syscon_ver = v6;
  }
  while ( v6 <= 0x802FF );
  keyring_writeX_80250C(0x508LL, (__int64)&syscon_ver, 4LL);
  model_info_2 = get_cached_model_info_8038F2();
  keyring_writeX_80250C(0x51BLL, (__int64)&model_info_2, 4LL);
  if ( (model_info_2 & 0xFF0000u) > 0x7FFFFF )
    v7 = 0x1030;
  else
    v7 = 0x10;
  dword_E3100180 = v7;
  set_status(67LL);

Model info is retrieved from a global set by syscon command 5. Some checks are performed to determine if the vita is a devkit, if it is then a flag to enable debug LEDs is set. Syscon hw version from command 1 is written to keyslot 0x508 and vita model info is written to keyslot 0x51B.

Retrieve boot type from syscon

  boot_type = 0;
  v8 = syscon_cmd_0x10_get_boot_type_80452A((__int64)&boot_type);
  v50 = v8;
  if ( (_DWORD)v8 )
  {
    v9 = 67LL;
LABEL_21:
    v10 = 0LL;
LABEL_22:
    report_error_808CAA(2LL, v9, v8, v10);
LABEL_23:
    ret = v50;
    goto LABEL_97;
  }
  set_status(68LL);

Boot type is retrieved from syscon cmd 0x10, 0xFF14 on cold boot or 0xFF80 on resume boot.

TODO

  ret_from_E002_1 = dword_E0020004;
  ret_from_E002 = ret_from_E002_1;
  ret_from_brom_1 = dword_E0010004;
  ret_from_brom = ret_from_brom_1;
  v15 = dword_E0062020;
  v8 = check_0x501_protection_and_compare_800DBC(ret_from_E002, ret_from_brom);
  v50 = v8;
  if ( (_DWORD)v8 )
  {
    v9 = 68LL;
    goto LABEL_21;
  }
  set_status(69LL);

Resume checks

  is_resume = (boot_type >> 7) & 1;
  v17 = something_with_dram_808662(1LL, is_resume);
  v50 = v17;
  if ( (_DWORD)v17 )
  {
    v18 = (unsigned __int8)sub_80877E();
    v19 = sub_808784();
    v9 = 69LL;
    v8 = v17;
    v10 = (v18 << 8) | (unsigned int)v19;
    goto LABEL_22;
  }
  if ( !(_DWORD)is_resume )
    goto LABEL_32;
  if ( !check_tz_magic_800A82() )
  {
    is_resume = 0LL;
LABEL_32:
    v20 = 0;
    clear_kbl_param_801C0C();
    goto LBL_123;
  }
  if ( copy_kbl_param_to_0x4001FD00_801C1E() )
  {
    is_resume = 1LL;
    goto LABEL_32;
  }
  is_resume = 1LL;
  v20 = 1;
LBL_123:
  emmc_key_reg = dword_E0070004;
  if ( emmc_key_reg & 1 )
  {
    set_status(82LL);
    report_error_808CAA(2LL, 82LL, 0x800F0029LL, 0LL);
    ret = 0x800F0029;
    goto LABEL_97;
  }
  sub_806B58(0x40000500LL, 0x1000LL);
  set_status(84LL);

Coldboot/resume is determined from bit 7 of boot_type returned by syscon. Then, something with dram??? If boot type is resume but TZ magic word (0x9E3199B7) isn't present, it changes boot type to coldboot.

If coldboot, KBL Param at 0x1F000100 is cleared, otherwise it's restored from 0x4001FD00. Then, some check on emmc crypto reg??? sub_806B58(0x40000500LL, 0x1000LL);???

Factory firmware check

  a3 = 0;
  v8 = get_factory_fw_801BAC((__int64)&a3);
  v50 = v8;
  if ( (_DWORD)v8 )
  {
    v9 = 84LL;
    goto LABEL_21;
  }
  v10 = a3;
  if ( a3 >= 0x3600001 )
  {
    v8 = 0x800F0030LL;
    v50 = 0x800F0030;
    v9 = 84LL;
    goto LABEL_22;
  }
  keyring_writeX_80250C(0x50FLL, (__int64)&a3, 4LL);
  set_status(85LL);

Factory firmware version is retrieved from idstorage and written to keyring slot 0x50F. The factory firmware must be not higher than this second_loader version, otherwise the boot is aborted to prevent downgrading below factory firmware version.

IDPS and OpenPSID

  ret = idps_and_openpsid_800B06();
  v50 = ret;
  if ( !ret )
  {
    set_status(95LL);

IDPS and OpenPSID are retrieved from idstorage and written to keyslots 0x509 and 0x50D.

TODO

    v22 = calls_syscon_0x88E_8089F0(6LL);
    v50 = v22;
    if ( (_DWORD)v22 )
      report_error_808CAA(1LL, 95LL, v22, 0LL);
    some_set_clock_808960(6LL);
    some_set_clock2_808982();
    v23 = 0x800F0026;
    if ( !(_DWORD)is_resume )
    {
      dmac_init_804BEC(&ctx);
      v23 = dmac_memset_int_804F04(0x40000000LL, 0x200000LL, 0LL, &ctx);
    }
    set_status(86LL);
    v24 = syscon_read_cmd_0x1082_ptr_0x480_into_gbuf_80232A();
    v50 = v24;
    if ( (_DWORD)v24 )
      report_error_808CAA(1LL, 86LL, v24, 0LL);
    if ( !v20 )
      syscon_read_cmd_0x1082_ptr_0x4a0_into_kbl_param_802346();
    if ( !(_DWORD)is_resume && !v23 )
      dmac_wait_804C16(&ctx);
    set_status(77LL);
    if ( (_DWORD)is_resume )
      v25 = syscon_ver > 0x1000101;
    else
      v25 = 0LL;
    v50 = derive_slots_and_set_0x50B_8023A2(v25);
    if ( v50 )
    {
      syscon_unk_808C2A();
      v9 = 77LL;
LABEL_77:
      v8 = (unsigned int)v50;
      goto LABEL_21;
    }
    set_status(81LL);
    v50 = pervasive_and_syscon_cmd_0x888_optional_801038(v26);
    if ( v50 )
    {
      syscon_unk_808C2A();
      v9 = 81LL;
      goto LABEL_77;
    }
    memset((__int64)line_0x510, 0LL, 0x20LL);
    if ( sub_802050() )
    {
      set_status(87LL);
      v27 = read_syscon_cmd_0x90_off_0xE0_802056((__int64)line_0x510);
      v50 = v27;
      if ( (_DWORD)v27 )
        report_error_808CAA(1LL, 87LL, v27, 0LL);
      if ( !is_bit_set_802088((__int64)line_0x510, 0x9FLL) && !is_bit_set_802088((__int64)line_0x510, 0x81LL) )
        sub_808B66(1LL);
    }
    write_kbl_param_fields_from_syscon_801FC0();
    keyring_writeX_80250C(0x510LL, (__int64)line_0x510, 0x20LL);
    memset((__int64)line_0x50A, 0LL, 0x10LL);
    set_keys_0x506_0x507_8020EA();
    if ( !is_bit_set_802088((__int64)line_0x510, 0xF0LL) )
    {
      set_status(88LL);
      v28 = some_syscon_derivation_802112((__int64)line_0x50A);
      v50 = v28;
      if ( (_DWORD)v28 != 0x800F0002 && (_DWORD)v28 != 0x800F0025 )
      {
        if ( (_DWORD)v28 )
        {
          report_error_808CAA(1LL, 88LL, v28, 0LL);
        }
        else if ( is_bit_set_802088((__int64)line_0x510, 241LL) )
        {
          unset_bit_8022F4((__int64)line_0x50A, 13LL, 1LL);
          unset_bit_8022F4((__int64)line_0x50A, 14LL, 1LL);
        }
      }
    }
    keyring_writeX_80250C(0x50ALL, (__int64)line_0x50A, 0x10LL);
    set_status(70LL);

No-op

    ret = zero_801B0E();
    v50 = ret;
    if ( !ret )
    {
      set_status(71LL);

No-op

      v50 = zero_801B24();
      if ( v50 )
      {
        syscon_unk_808C2A();
        report_error_808CAA(2LL, 71LL, (unsigned int)v50, 0LL);
        set_status(79LL);
        goto LABEL_23;
      }
      set_status(73LL);

kbl decryption

      kbl_fw_version = 0;
      if ( (_DWORD)is_resume && (boot_type & 0x7F) != 0x17 )
      {
        copy_arm_tz_reset_vectors_800A9A();
      }
      else
      {
        v50 = decrypt_kernel_boot_loader_self_801162((__int64)&kbl_fw_version);
        if ( v50 )
        {
          syscon_unk_808C2A();
          v9 = 73LL;
          goto LABEL_77;
        }
      }
      set_status(89LL);

If resume, ARM exception vectors are copied from TZ memory at 0x40000000 to 0x1F000000 (0x0 alias on ARM). If coldboot, kernel_boot_loader.self is loaded from emmc and decrypted.

TODO

      syscon_unk_808C2A();
      print_info_log_800A5E();
      seven = ret_7_801B2C();
      six = ret_6_801B30();
      *(_DWORD *)(unsigned int)&dword_80C698 = zero_801B34();
      sub_804764((unsigned int)&unk_801888);
      set_status(78LL);

Check kbl version integrity

      v8 = set_and_check_current_fw_version_800E74(kbl_fw_version, is_resume);
      v50 = v8;
      if ( (_DWORD)v8 )
      {
        v9 = 78LL;
        goto LABEL_21;
      }
      sub_804786((__int64)&v50);
      v8 = (unsigned int)v50;
      if ( v50 )
      {
        v9 = 74LL;
        goto LABEL_21;
      }
      set_status(94LL);

??? If coldboot, makes sure that kbl version from SELF matches this second_loader version. Also writes current version to keyslots 0x50E and 0x518. ???

Check kbl fw version vs factory version

      if ( (_DWORD)is_resume || kbl_fw_version >= a3 )
      {
        set_status(90LL);

If coldboot, make sure that kbl version is not lower than factory firmware version.

Write KBL Param fields

        write_kbl_param_801C36((__int64)line_0x510, (__int64)line_0x50A, boot_type, is_resume, a3);
        set_status(96LL);

Writes most of KBL Param fields ???

TODO

        v29 = reads_pervasivevid_calls_syscon_0x88E_80899C(seven);
        v50 = v29;
        if ( (_DWORD)v29 )
          report_error_808CAA(1LL, 76LL, v29, 0LL);
        set_status(76LL);

Prepares to start ARM

        v8 = prepare_to_start_arm_80878A(seven, *(unsigned int *)(unsigned int)&dword_80C698, 0LL);
        v50 = v8;
        if ( (_DWORD)v8 )
        {
          v9 = 76LL;
          goto LABEL_21;
        }
        v30 = some_line;
        set_status(80LL);

Some dance with ARM clock/pervasive stuff ???

TODO

        read_line32_8003E8(0x602LL, (__int64)some_line);
        v31 = 0xE0020100LL;
        v32 = some_line;
        v33 = 8;
        do
        {
          v34 = *(_DWORD *)v32;
          v32 = (char *)(unsigned int)((_DWORD)v32 + 4);
          *(_DWORD *)v31 = v34;
          v31 = (unsigned int)(v31 + 4);
          --v33;
        }
        while ( v33 );
        read_line32_8003E8(0x601LL, (__int64)some_line);
        v35 = 8;
        do
        {
          v36 = *(_DWORD *)v30;
          v30 = (char *)(unsigned int)((_DWORD)v30 + 4);
          *(_DWORD *)v31 = v36;
          v31 = (unsigned int)(v31 + 4);
          --v35;
        }
        while ( v35 );
        nullsub_2();
        if ( six < 7 )
        {
          if ( six != 6 )
          {
            some_set_clock_808960(six);
            calls_syscon_0x88E_8089F0(six);
          }
        }
        else
        {
          calls_syscon_0x88E_8089F0(six);
          some_set_clock_808960(six);
        }
      }
      else
      {
        report_error_808CAA(2LL, 94LL, a3, kbl_fw_version);
        ret = 0x800F0030;
      }
    }
  }
LABEL_97:
  protect_lines_and_set_E002_8010CE();
  if ( ret )
  {
    send_status_to_arm_8010E2(0LL);
    print_info_log_800A5E();
    set_number_base_808D40((unsigned int)&g_unused, 0x10LL);
    printnum_808D46(2LL);
    printf((unsigned int)aB1B004fa16);          // boot failed
    randnum = dword_E005003C;
    sleep_8051D0((unsigned __int16)randnum);
    if ( !sub_808B82() && sub_803744() )
      syscon_cmd_0xC0_804606(0LL, 0LL);
  }
  else
  {
    send_status_to_arm_8010E2(1LL);
    syncm_8003E0();
    set_status(64LL);
  }
  return ret;
}

Keyrings protection

On FWs 0.995-3.60 the following keyrings are protected with flag 0x1C1F (so cmep read disabled) after starting ARM: 0x0-0x7F, 0x140-0x17F, 0x200-0x203, 0x206-0x20D, 0x344-0x353, 0x400-0x47F, 0x502-0x57F, 0x700-0x77F.

Bypassing version checks

Cmep keyring 0x50B (mgmt data) offset 0x4 bit 1 set = ignore version mismatch errors. This keyring itself is set from reading SNVS block 0 using Syscon command 0xD2. Alternatively set version to 0xDEADBEEF to skip version checks.

Session key/coredump encryption

0x20 random bytes are generated and written to keyslot 0x51A. Then, the buffer is encrypted with aes128-cbc using coredump_key and coredump_iv. The result is copied to KBL Param +0x100 (0x1F000200)