2018-11-27 09:45:43 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 naehrwert
|
2021-02-06 00:55:58 +00:00
|
|
|
* Copyright (c) 2018-2021 CTCaer
|
2018-11-27 09:45:43 +00:00
|
|
|
* Copyright (c) 2018 balika011
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "fe_info.h"
|
2020-06-14 13:45:45 +00:00
|
|
|
#include <gfx_utils.h>
|
2018-11-27 09:45:43 +00:00
|
|
|
#include "../hos/hos.h"
|
|
|
|
#include "../hos/pkg1.h"
|
2020-06-14 13:45:45 +00:00
|
|
|
#include <libs/fatfs/ff.h>
|
|
|
|
#include <mem/heap.h>
|
|
|
|
#include <mem/smmu.h>
|
|
|
|
#include <power/bq24193.h>
|
|
|
|
#include <power/max17050.h>
|
2021-02-06 00:55:58 +00:00
|
|
|
#include <sec/se_t210.h>
|
2020-06-14 13:45:45 +00:00
|
|
|
#include <sec/tsec.h>
|
|
|
|
#include <soc/fuse.h>
|
|
|
|
#include <soc/i2c.h>
|
|
|
|
#include <soc/kfuse.h>
|
|
|
|
#include <soc/t210.h>
|
|
|
|
#include <storage/mmc.h>
|
2018-11-27 09:45:43 +00:00
|
|
|
#include "../storage/nx_emmc.h"
|
2020-06-14 13:45:45 +00:00
|
|
|
#include <storage/nx_sd.h>
|
|
|
|
#include <storage/sdmmc.h>
|
|
|
|
#include <utils/btn.h>
|
|
|
|
#include <utils/util.h>
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
extern void emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage);
|
|
|
|
|
2019-06-30 00:15:46 +00:00
|
|
|
#pragma GCC push_options
|
2019-10-18 15:02:06 +00:00
|
|
|
#pragma GCC optimize ("Os")
|
2019-06-30 00:15:46 +00:00
|
|
|
|
2018-11-27 09:45:43 +00:00
|
|
|
void print_fuseinfo()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("\nSKU: %X - ", FUSE(FUSE_SKU_INFO));
|
2020-12-26 14:34:12 +00:00
|
|
|
switch (fuse_read_hw_state())
|
2018-11-27 09:45:43 +00:00
|
|
|
{
|
2020-12-26 14:34:12 +00:00
|
|
|
case FUSE_NX_HW_STATE_PROD:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Retail\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
2020-12-26 14:34:12 +00:00
|
|
|
case FUSE_NX_HW_STATE_DEV:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Dev\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-12-26 14:34:12 +00:00
|
|
|
gfx_printf("Sdram ID: %d\n", fuse_read_dramid(true));
|
|
|
|
gfx_printf("Burnt fuses: %d / 64\n", fuse_count_burnt(fuse_read_odm(7)));
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Secure key: %08X%08X%08X%08X\n\n\n",
|
2018-11-27 09:45:43 +00:00
|
|
|
byte_swap_32(FUSE(FUSE_PRIVATE_KEY0)), byte_swap_32(FUSE(FUSE_PRIVATE_KEY1)),
|
|
|
|
byte_swap_32(FUSE(FUSE_PRIVATE_KEY2)), byte_swap_32(FUSE(FUSE_PRIVATE_KEY3)));
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%k(Unlocked) fuse cache:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC);
|
2019-06-30 00:15:46 +00:00
|
|
|
gfx_hexdump(0x7000F900, (u8 *)0x7000F900, 0x300);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
u32 btn = btn_wait();
|
|
|
|
if (btn & BTN_POWER)
|
|
|
|
{
|
|
|
|
if (sd_mount())
|
|
|
|
{
|
|
|
|
char path[64];
|
2019-06-30 00:15:46 +00:00
|
|
|
emmcsn_path_impl(path, "/dumps", "fuse_cached.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)0x7000F900, 0x300, path))
|
|
|
|
gfx_puts("\nfuse_cached.bin saved!\n");
|
|
|
|
|
|
|
|
u32 words[192];
|
|
|
|
fuse_read_array(words);
|
|
|
|
emmcsn_path_impl(path, "/dumps", "fuse_array_raw.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)words, sizeof(words), path))
|
|
|
|
gfx_puts("\nfuse_array_raw.bin saved!\n");
|
|
|
|
|
2020-06-13 15:32:40 +00:00
|
|
|
sd_end();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_kfuseinfo()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kKFuse contents:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-11-27 09:45:43 +00:00
|
|
|
u32 buf[KFUSE_NUM_WORDS];
|
|
|
|
if (!kfuse_read(buf))
|
|
|
|
EPRINTF("CRC fail.");
|
|
|
|
else
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_hexdump(0, (u8 *)buf, KFUSE_NUM_WORDS * 4);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
u32 btn = btn_wait();
|
|
|
|
if (btn & BTN_POWER)
|
|
|
|
{
|
|
|
|
if (sd_mount())
|
|
|
|
{
|
|
|
|
char path[64];
|
|
|
|
emmcsn_path_impl(path, "/dumps", "kfuses.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)buf, KFUSE_NUM_WORDS * 4, path))
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nDone!\n");
|
2020-06-13 15:32:40 +00:00
|
|
|
sd_end();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_mmc_info()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
static const u32 SECTORS_TO_MIB_COEFF = 11;
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
if (!sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400))
|
2018-11-27 09:45:43 +00:00
|
|
|
{
|
|
|
|
EPRINTF("Failed to init eMMC.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
u16 card_type;
|
|
|
|
u32 speed = 0;
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kCID:%k\n", 0xFF00DDFF, 0xFFCCCCCC);
|
2021-02-06 01:19:42 +00:00
|
|
|
switch (emmc_storage.csd.mmca_vsn)
|
2018-11-27 09:45:43 +00:00
|
|
|
{
|
|
|
|
case 2: /* MMC v2.0 - v2.2 */
|
|
|
|
case 3: /* MMC v3.1 - v3.3 */
|
|
|
|
case 4: /* MMC v4 */
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(
|
2018-11-27 09:45:43 +00:00
|
|
|
" Vendor ID: %X\n"
|
|
|
|
" OEM ID: %02X\n"
|
|
|
|
" Model: %c%c%c%c%c%c\n"
|
|
|
|
" Prd Rev: %X\n"
|
|
|
|
" S/N: %04X\n"
|
|
|
|
" Month/Year: %02d/%04d\n\n",
|
2021-02-06 01:41:35 +00:00
|
|
|
emmc_storage.cid.manfid, emmc_storage.cid.oemid,
|
2021-02-06 01:19:42 +00:00
|
|
|
emmc_storage.cid.prod_name[0], emmc_storage.cid.prod_name[1], emmc_storage.cid.prod_name[2],
|
|
|
|
emmc_storage.cid.prod_name[3], emmc_storage.cid.prod_name[4], emmc_storage.cid.prod_name[5],
|
|
|
|
emmc_storage.cid.prv, emmc_storage.cid.serial, emmc_storage.cid.month, emmc_storage.cid.year);
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
if (emmc_storage.csd.structure == 0)
|
2018-11-27 09:45:43 +00:00
|
|
|
EPRINTF("Unknown CSD structure.");
|
|
|
|
else
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kExtended CSD V1.%d:%k\n",
|
2021-02-06 01:19:42 +00:00
|
|
|
0xFF00DDFF, emmc_storage.ext_csd.ext_struct, 0xFFCCCCCC);
|
|
|
|
card_type = emmc_storage.ext_csd.card_type;
|
2019-12-04 13:56:53 +00:00
|
|
|
char card_type_support[96];
|
2018-11-27 09:45:43 +00:00
|
|
|
card_type_support[0] = 0;
|
|
|
|
if (card_type & EXT_CSD_CARD_TYPE_HS_26)
|
|
|
|
{
|
2019-12-04 13:56:53 +00:00
|
|
|
strcat(card_type_support, "HS26");
|
2018-11-27 09:45:43 +00:00
|
|
|
speed = (26 << 16) | 26;
|
|
|
|
}
|
|
|
|
if (card_type & EXT_CSD_CARD_TYPE_HS_52)
|
|
|
|
{
|
2019-12-04 13:56:53 +00:00
|
|
|
strcat(card_type_support, ", HS52");
|
2018-11-27 09:45:43 +00:00
|
|
|
speed = (52 << 16) | 52;
|
|
|
|
}
|
|
|
|
if (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
|
|
|
{
|
2019-12-04 13:56:53 +00:00
|
|
|
strcat(card_type_support, ", DDR52_1.8V");
|
2018-11-27 09:45:43 +00:00
|
|
|
speed = (52 << 16) | 104;
|
|
|
|
}
|
|
|
|
if (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
|
|
|
|
{
|
2019-12-04 13:56:53 +00:00
|
|
|
strcat(card_type_support, ", HS200_1.8V");
|
2018-11-27 09:45:43 +00:00
|
|
|
speed = (200 << 16) | 200;
|
|
|
|
}
|
|
|
|
if (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)
|
|
|
|
{
|
2019-12-04 13:56:53 +00:00
|
|
|
strcat(card_type_support, ", HS400_1.8V");
|
2018-11-27 09:45:43 +00:00
|
|
|
speed = (200 << 16) | 400;
|
|
|
|
}
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(
|
2018-11-27 09:45:43 +00:00
|
|
|
" Spec Version: %02X\n"
|
|
|
|
" Extended Rev: 1.%d\n"
|
|
|
|
" Dev Version: %d\n"
|
|
|
|
" Cmd Classes: %02X\n"
|
|
|
|
" Capacity: %s\n"
|
|
|
|
" Max Rate: %d MB/s (%d MHz)\n"
|
|
|
|
" Current Rate: %d MB/s\n"
|
|
|
|
" Type Support: ",
|
2021-02-06 01:19:42 +00:00
|
|
|
emmc_storage.csd.mmca_vsn, emmc_storage.ext_csd.rev, emmc_storage.ext_csd.dev_version, emmc_storage.csd.cmdclass,
|
|
|
|
emmc_storage.csd.capacity == (4096 * 512) ? "High" : "Low", speed & 0xFFFF, (speed >> 16) & 0xFFFF,
|
|
|
|
emmc_storage.csd.busspeed);
|
2018-11-27 09:45:43 +00:00
|
|
|
gfx_con.fntsz = 8;
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%s", card_type_support);
|
2018-11-27 09:45:43 +00:00
|
|
|
gfx_con.fntsz = 16;
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("\n\n", card_type_support);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
u32 boot_size = emmc_storage.ext_csd.boot_mult << 17;
|
|
|
|
u32 rpmb_size = emmc_storage.ext_csd.rpmb_mult << 17;
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%keMMC Partitions:%k\n", 0xFF00DDFF, 0xFFCCCCCC);
|
|
|
|
gfx_printf(" 1: %kBOOT0 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC,
|
2019-06-30 00:15:46 +00:00
|
|
|
boot_size / 1024, boot_size / 512);
|
2019-04-21 14:33:39 +00:00
|
|
|
gfx_put_small_sep();
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" 2: %kBOOT1 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC,
|
2019-06-30 00:15:46 +00:00
|
|
|
boot_size / 1024, boot_size / 512);
|
2019-04-21 14:33:39 +00:00
|
|
|
gfx_put_small_sep();
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" 3: %kRPMB %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC,
|
2019-06-30 00:15:46 +00:00
|
|
|
rpmb_size / 1024, rpmb_size / 512);
|
2019-04-21 14:33:39 +00:00
|
|
|
gfx_put_small_sep();
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" 0: %kGPP (USER) %k\n Size: %5d MiB (LBA Sectors: 0x%07X)\n\n", 0xFF96FF00, 0xFFCCCCCC,
|
2021-02-06 01:19:42 +00:00
|
|
|
emmc_storage.sec_cnt >> SECTORS_TO_MIB_COEFF, emmc_storage.sec_cnt);
|
2019-04-21 14:33:39 +00:00
|
|
|
gfx_put_small_sep();
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kGPP (eMMC USER) partition table:%k\n", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_set_mmc_partition(&emmc_storage, EMMC_GPP);
|
2018-11-27 09:45:43 +00:00
|
|
|
LIST_INIT(gpt);
|
2021-02-06 01:19:42 +00:00
|
|
|
nx_emmc_gpt_parse(&gpt, &emmc_storage);
|
2018-11-27 09:45:43 +00:00
|
|
|
int gpp_idx = 0;
|
|
|
|
LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link)
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" %02d: %k%s%k\n Size: % 5d MiB (LBA Sectors 0x%07X)\n LBA Range: %08X-%08X\n",
|
2018-11-27 09:45:43 +00:00
|
|
|
gpp_idx++, 0xFFAEFD14, part->name, 0xFFCCCCCC, (part->lba_end - part->lba_start + 1) >> SECTORS_TO_MIB_COEFF,
|
|
|
|
part->lba_end - part->lba_start + 1, part->lba_start, part->lba_end);
|
2019-04-21 14:33:39 +00:00
|
|
|
gfx_put_small_sep();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
nx_emmc_gpt_free(&gpt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_end(&emmc_storage);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_sdcard_info()
|
|
|
|
{
|
|
|
|
static const u32 SECTORS_TO_MIB_COEFF = 11;
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2020-08-15 10:23:11 +00:00
|
|
|
if (sd_initialize(true))
|
2018-11-27 09:45:43 +00:00
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kCard IDentification:%k\n", 0xFF00DDFF, 0xFFCCCCCC);
|
|
|
|
gfx_printf(
|
2018-11-27 09:45:43 +00:00
|
|
|
" Vendor ID: %02x\n"
|
|
|
|
" OEM ID: %c%c\n"
|
|
|
|
" Model: %c%c%c%c%c\n"
|
|
|
|
" HW rev: %X\n"
|
|
|
|
" FW rev: %X\n"
|
|
|
|
" S/N: %08x\n"
|
|
|
|
" Month/Year: %02d/%04d\n\n",
|
|
|
|
sd_storage.cid.manfid, (sd_storage.cid.oemid >> 8) & 0xFF, sd_storage.cid.oemid & 0xFF,
|
|
|
|
sd_storage.cid.prod_name[0], sd_storage.cid.prod_name[1], sd_storage.cid.prod_name[2],
|
|
|
|
sd_storage.cid.prod_name[3], sd_storage.cid.prod_name[4],
|
|
|
|
sd_storage.cid.hwrev, sd_storage.cid.fwrev, sd_storage.cid.serial,
|
|
|
|
sd_storage.cid.month, sd_storage.cid.year);
|
|
|
|
|
2020-08-15 10:23:11 +00:00
|
|
|
u16 *sd_errors = sd_get_error_count();
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kCard-Specific Data V%d.0:%k\n", 0xFF00DDFF, sd_storage.csd.structure + 1, 0xFFCCCCCC);
|
|
|
|
gfx_printf(
|
2018-11-27 09:45:43 +00:00
|
|
|
" Cmd Classes: %02X\n"
|
|
|
|
" Capacity: %d MiB\n"
|
|
|
|
" Bus Width: %d\n"
|
|
|
|
" Current Rate: %d MB/s (%d MHz)\n"
|
|
|
|
" Speed Class: %d\n"
|
|
|
|
" UHS Grade: U%d\n"
|
|
|
|
" Video Class: V%d\n"
|
|
|
|
" App perf class: A%d\n"
|
2020-08-15 10:23:11 +00:00
|
|
|
" Write Protect: %d\n"
|
|
|
|
" SDMMC Errors: %d %d %d\n\n",
|
2020-03-03 02:11:13 +00:00
|
|
|
sd_storage.csd.cmdclass, sd_storage.sec_cnt >> 11,
|
2018-11-27 09:45:43 +00:00
|
|
|
sd_storage.ssr.bus_width, sd_storage.csd.busspeed, sd_storage.csd.busspeed * 2,
|
|
|
|
sd_storage.ssr.speed_class, sd_storage.ssr.uhs_grade, sd_storage.ssr.video_class,
|
2020-08-15 10:23:11 +00:00
|
|
|
sd_storage.ssr.app_class, sd_storage.csd.write_protect,
|
|
|
|
sd_errors[0], sd_errors[1], sd_errors[2]); // SD_ERROR_INIT_FAIL, SD_ERROR_RW_FAIL, SD_ERROR_RW_RETRY.
|
|
|
|
|
|
|
|
int res = f_mount(&sd_fs, "", 1);
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
gfx_puts("Acquiring FAT volume info...\n\n");
|
|
|
|
f_getfree("", &sd_fs.free_clst, NULL);
|
|
|
|
gfx_printf("%kFound %s volume:%k\n Free: %d MiB\n Cluster: %d KiB\n",
|
|
|
|
0xFF00DDFF, sd_fs.fs_type == FS_EXFAT ? "exFAT" : "FAT32", 0xFFCCCCCC,
|
|
|
|
sd_fs.free_clst * sd_fs.csize >> SECTORS_TO_MIB_COEFF, (sd_fs.csize > 1) ? (sd_fs.csize >> 1) : 512);
|
|
|
|
f_mount(NULL, "", 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\n"
|
|
|
|
"Make sure that a FAT partition exists..", res);
|
|
|
|
}
|
|
|
|
|
|
|
|
sdmmc_storage_end(&sd_storage);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EPRINTF("Failed to init SD card.");
|
|
|
|
if (!sdmmc_get_sd_inserted())
|
|
|
|
EPRINTF("Make sure that it is inserted.");
|
|
|
|
else
|
|
|
|
EPRINTF("SD Card Reader is not properly seated!");
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_tsec_key()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2018-12-02 09:11:07 +00:00
|
|
|
u32 retries = 0;
|
|
|
|
|
|
|
|
tsec_ctxt_t tsec_ctxt;
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_init_mmc(&emmc_storage, &emmc_sdmmc, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_HS400);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
// Read package1.
|
|
|
|
u8 *pkg1 = (u8 *)malloc(0x40000);
|
2021-02-06 01:19:42 +00:00
|
|
|
sdmmc_storage_set_mmc_partition(&emmc_storage, EMMC_BOOT0);
|
|
|
|
sdmmc_storage_read(&emmc_storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1);
|
|
|
|
sdmmc_storage_end(&emmc_storage);
|
2018-11-27 09:45:43 +00:00
|
|
|
const pkg1_id_t *pkg1_id = pkg1_identify(pkg1);
|
|
|
|
if (!pkg1_id)
|
|
|
|
{
|
2020-04-30 00:25:22 +00:00
|
|
|
EPRINTF("Unknown pkg1 version.");
|
2018-11-27 09:45:43 +00:00
|
|
|
goto out_wait;
|
|
|
|
}
|
|
|
|
|
2021-02-06 00:55:58 +00:00
|
|
|
u8 keys[SE_KEY_128_SIZE * 2];
|
2019-02-23 22:59:33 +00:00
|
|
|
memset(keys, 0x00, 0x20);
|
2018-12-02 09:11:07 +00:00
|
|
|
|
|
|
|
tsec_ctxt.fw = (u8 *)pkg1 + pkg1_id->tsec_off;
|
|
|
|
tsec_ctxt.pkg1 = pkg1;
|
|
|
|
tsec_ctxt.pkg11_off = pkg1_id->pkg11_off;
|
|
|
|
tsec_ctxt.secmon_base = pkg1_id->secmon_base;
|
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
if (pkg1_id->kb <= KB_FIRMWARE_VERSION_600)
|
|
|
|
tsec_ctxt.size = 0xF00;
|
|
|
|
else if (pkg1_id->kb == KB_FIRMWARE_VERSION_620)
|
2018-12-02 09:11:07 +00:00
|
|
|
tsec_ctxt.size = 0x2900;
|
2019-06-30 00:29:46 +00:00
|
|
|
else if (pkg1_id->kb == KB_FIRMWARE_VERSION_700)
|
2019-02-23 22:59:33 +00:00
|
|
|
{
|
|
|
|
tsec_ctxt.size = 0x3000;
|
|
|
|
// Exit after TSEC key generation.
|
|
|
|
*((vu16 *)((u32)tsec_ctxt.fw + 0x2DB5)) = 0x02F8;
|
|
|
|
}
|
2019-06-30 00:29:46 +00:00
|
|
|
else
|
|
|
|
tsec_ctxt.size = 0x3300;
|
2019-02-23 22:59:33 +00:00
|
|
|
|
|
|
|
if (pkg1_id->kb == KB_FIRMWARE_VERSION_620)
|
|
|
|
{
|
2018-12-02 09:11:07 +00:00
|
|
|
u8 *tsec_paged = (u8 *)page_alloc(3);
|
|
|
|
memcpy(tsec_paged, (void *)tsec_ctxt.fw, tsec_ctxt.size);
|
|
|
|
tsec_ctxt.fw = tsec_paged;
|
|
|
|
}
|
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
int res = 0;
|
2018-12-02 09:11:07 +00:00
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
while (tsec_query(keys, pkg1_id->kb, &tsec_ctxt) < 0)
|
|
|
|
{
|
|
|
|
memset(keys, 0x00, 0x20);
|
2018-12-02 09:11:07 +00:00
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
retries++;
|
2018-12-02 09:11:07 +00:00
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
if (retries > 3)
|
2018-12-02 09:11:07 +00:00
|
|
|
{
|
2019-02-23 22:59:33 +00:00
|
|
|
res = -1;
|
|
|
|
break;
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
2019-02-23 22:59:33 +00:00
|
|
|
}
|
2018-12-02 09:11:07 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%kTSEC key: %k", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-12-02 09:11:07 +00:00
|
|
|
|
2019-02-23 22:59:33 +00:00
|
|
|
if (res >= 0)
|
|
|
|
{
|
2021-02-06 00:55:58 +00:00
|
|
|
for (u32 j = 0; j < SE_KEY_128_SIZE; j++)
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%02X", keys[j]);
|
2019-02-23 22:59:33 +00:00
|
|
|
|
|
|
|
if (pkg1_id->kb == KB_FIRMWARE_VERSION_620)
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("\n%kTSEC root: %k", 0xFF00DDFF, 0xFFCCCCCC);
|
2021-02-06 00:55:58 +00:00
|
|
|
for (u32 j = 0; j < SE_KEY_128_SIZE; j++)
|
|
|
|
gfx_printf("%02X", keys[SE_KEY_128_SIZE + j]);
|
2019-02-23 22:59:33 +00:00
|
|
|
}
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
2019-02-23 22:59:33 +00:00
|
|
|
else
|
|
|
|
EPRINTFARGS("ERROR %X\n", res);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\n\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
u32 btn = btn_wait();
|
|
|
|
if (btn & BTN_POWER)
|
|
|
|
{
|
|
|
|
if (sd_mount())
|
|
|
|
{
|
|
|
|
char path[64];
|
|
|
|
emmcsn_path_impl(path, "/dumps", "tsec_keys.bin", NULL);
|
2021-02-06 00:55:58 +00:00
|
|
|
if (!sd_save_to_file(keys, SE_KEY_128_SIZE * 2, path))
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nDone!\n");
|
2020-06-13 15:32:40 +00:00
|
|
|
sd_end();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out_wait:
|
|
|
|
btn_wait();
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(pkg1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_fuel_gauge_info()
|
|
|
|
{
|
|
|
|
int value = 0;
|
|
|
|
|
2020-04-30 00:25:22 +00:00
|
|
|
gfx_printf("%kFuel Gauge Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_RepSOC, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Capacity now: %3d%\n", value >> 8);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_RepCap, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Capacity now: %4d mAh\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_FullCAP, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Capacity full: %4d mAh\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_DesignCap, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Capacity (design): %4d mAh\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_Current, &value);
|
|
|
|
if (value >= 0)
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Current now: %d mA\n", value / 1000);
|
2018-11-27 09:45:43 +00:00
|
|
|
else
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Current now: -%d mA\n", ~value / 1000);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_AvgCurrent, &value);
|
|
|
|
if (value >= 0)
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Current average: %d mA\n", value / 1000);
|
2018-11-27 09:45:43 +00:00
|
|
|
else
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Current average: -%d mA\n", ~value / 1000);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_VCELL, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Voltage now: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_OCVInternal, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Voltage open-circuit: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_MinVolt, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Min voltage reached: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_MaxVolt, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Max voltage reached: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_V_empty, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Empty voltage (design): %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
max17050_get_property(MAX17050_TEMP, &value);
|
|
|
|
if (value >= 0)
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Battery temperature: %d.%d oC\n", value / 10, value % 10);
|
2018-11-27 09:45:43 +00:00
|
|
|
else
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Battery temperature: -%d.%d oC\n", ~value / 10, (~value) % 10);
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void print_battery_charger_info()
|
|
|
|
{
|
|
|
|
int value = 0;
|
|
|
|
|
2020-04-30 00:25:22 +00:00
|
|
|
gfx_printf("%k\n\nBattery Charger Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_InputVoltageLimit, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Input voltage limit: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_InputCurrentLimit, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Input current limit: %4d mA\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_SystemMinimumVoltage, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Min voltage limit: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_FastChargeCurrentLimit, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Fast charge current limit: %4d mA\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_ChargeVoltageLimit, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Charge voltage limit: %4d mV\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
bq24193_get_property(BQ24193_ChargeStatus, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Charge status: ");
|
2018-11-27 09:45:43 +00:00
|
|
|
switch (value)
|
|
|
|
{
|
|
|
|
case 0:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Not charging\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Pre-charging\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Fast charging\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Charge terminated\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
default:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Unknown (%d)\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
bq24193_get_property(BQ24193_TempStatus, &value);
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Temperature status: ");
|
2018-11-27 09:45:43 +00:00
|
|
|
switch (value)
|
|
|
|
{
|
|
|
|
case 0:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Normal\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Warm\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Cool\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 5:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Cold\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
case 6:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Hot\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
default:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("Unknown (%d)\n", value);
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void print_battery_info()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
print_fuel_gauge_info();
|
|
|
|
|
|
|
|
print_battery_charger_info();
|
|
|
|
|
|
|
|
u8 *buf = (u8 *)malloc(0x100 * 2);
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%k\n\nBattery Fuel Gauge Registers:\n%k", 0xFF00DDFF, 0xFFCCCCCC);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 0x200; i += 2)
|
|
|
|
{
|
2019-06-30 00:15:46 +00:00
|
|
|
i2c_recv_buf_small(buf + i, 2, I2C_1, MAXIM17050_I2C_ADDR, i >> 1);
|
2018-11-27 09:45:43 +00:00
|
|
|
usleep(2500);
|
|
|
|
}
|
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_hexdump(0, (u8 *)buf, 0x200);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
u32 btn = btn_wait();
|
|
|
|
|
|
|
|
if (btn & BTN_POWER)
|
|
|
|
{
|
|
|
|
if (sd_mount())
|
|
|
|
{
|
|
|
|
char path[64];
|
|
|
|
emmcsn_path_impl(path, "/dumps", "fuel_gauge.bin", NULL);
|
|
|
|
if (sd_save_to_file((u8 *)buf, 0x200, path))
|
|
|
|
EPRINTF("\nError creating fuel.bin file.");
|
|
|
|
else
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nDone!\n");
|
2020-06-13 15:32:40 +00:00
|
|
|
sd_end();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _ipatch_process(u32 offset, u32 value)
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf("%8x %8x", BOOTROM_BASE + offset, value);
|
2018-11-27 09:45:43 +00:00
|
|
|
u8 lo = value & 0xff;
|
|
|
|
switch (value >> 8)
|
|
|
|
{
|
|
|
|
case 0x20:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" MOVS R0, #0x%02X", lo);
|
2018-12-16 14:52:38 +00:00
|
|
|
break;
|
|
|
|
case 0xDF:
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_printf(" SVC #0x%02X", lo);
|
2018-11-27 09:45:43 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void bootrom_ipatches_info()
|
|
|
|
{
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_clear_partial_grey(0x1B, 0, 1256);
|
|
|
|
gfx_con_setpos(0, 0);
|
2018-11-27 09:45:43 +00:00
|
|
|
|
|
|
|
static const u32 BOOTROM_SIZE = 0x18000;
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-11-27 09:45:43 +00:00
|
|
|
u32 res = fuse_read_ipatch(_ipatch_process);
|
|
|
|
if (res != 0)
|
|
|
|
EPRINTFARGS("Failed to read ipatches. Error: %d", res);
|
2018-12-16 14:52:38 +00:00
|
|
|
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n");
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-11-27 09:45:43 +00:00
|
|
|
u32 btn = btn_wait();
|
|
|
|
if (btn & BTN_POWER)
|
|
|
|
{
|
|
|
|
if (sd_mount())
|
|
|
|
{
|
|
|
|
char path[64];
|
|
|
|
u32 iram_evp_thunks[0x200];
|
|
|
|
u32 iram_evp_thunks_len = sizeof(iram_evp_thunks);
|
|
|
|
res = fuse_read_evp_thunk(iram_evp_thunks, &iram_evp_thunks_len);
|
|
|
|
if (res == 0)
|
|
|
|
{
|
|
|
|
emmcsn_path_impl(path, "/dumps", "evp_thunks.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)iram_evp_thunks, iram_evp_thunks_len, path))
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nevp_thunks.bin saved!\n");
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
EPRINTFARGS("Failed to read evp_thunks. Error: %d", res);
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-11-27 09:45:43 +00:00
|
|
|
emmcsn_path_impl(path, "/dumps", "bootrom_patched.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path))
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nbootrom_patched.bin saved!\n");
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-12-16 14:52:38 +00:00
|
|
|
u32 ipatch_backup[14];
|
|
|
|
memcpy(ipatch_backup, (void *)IPATCH_BASE, sizeof(ipatch_backup));
|
2018-12-15 15:56:45 +00:00
|
|
|
memset((void*)IPATCH_BASE, 0, sizeof(ipatch_backup));
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-11-27 09:45:43 +00:00
|
|
|
emmcsn_path_impl(path, "/dumps", "bootrom_unpatched.bin", NULL);
|
|
|
|
if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path))
|
2019-04-13 23:30:14 +00:00
|
|
|
gfx_puts("\nbootrom_unpatched.bin saved!\n");
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2018-12-15 15:56:45 +00:00
|
|
|
memcpy((void*)IPATCH_BASE, ipatch_backup, sizeof(ipatch_backup));
|
2019-10-18 15:02:06 +00:00
|
|
|
|
2020-06-13 15:32:40 +00:00
|
|
|
sd_end();
|
2018-11-27 09:45:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btn_wait();
|
|
|
|
}
|
|
|
|
}
|
2019-06-30 00:15:46 +00:00
|
|
|
|
|
|
|
#pragma GCC pop_options
|