From 471c1e514c3b826009dcf2d727a81f5c3de7e797 Mon Sep 17 00:00:00 2001 From: Kostas Missos Date: Tue, 27 Nov 2018 11:45:43 +0200 Subject: [PATCH] Major refactoring of main.c --- Makefile | 4 + bootloader/frontend/fe_emmc_tools.c | 884 +++++++++++ bootloader/frontend/fe_emmc_tools.h | 30 + bootloader/frontend/fe_info.c | 644 ++++++++ bootloader/frontend/fe_info.h | 32 + bootloader/frontend/fe_tools.c | 582 +++++++ bootloader/frontend/fe_tools.h | 27 + bootloader/main.c | 2247 +-------------------------- bootloader/soc/hw_init.c | 258 +++ bootloader/soc/hw_init.h | 26 + bootloader/soc/t210.h | 3 + 11 files changed, 2518 insertions(+), 2219 deletions(-) create mode 100644 bootloader/frontend/fe_emmc_tools.c create mode 100644 bootloader/frontend/fe_emmc_tools.h create mode 100644 bootloader/frontend/fe_info.c create mode 100644 bootloader/frontend/fe_info.h create mode 100644 bootloader/frontend/fe_tools.c create mode 100644 bootloader/frontend/fe_tools.h create mode 100644 bootloader/soc/hw_init.c create mode 100644 bootloader/soc/hw_init.h diff --git a/Makefile b/Makefile index 0559e9c..8a00ea5 100755 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ VPATH = $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/* OBJS = $(addprefix $(BUILD)/$(TARGET)/, \ start.o \ main.o \ + fe_emmc_tools.o \ + fe_info.o \ + fe_tools.o \ config.o \ btn.o \ clock.o \ @@ -43,6 +46,7 @@ OBJS = $(addprefix $(BUILD)/$(TARGET)/, \ se.o \ tsec.o \ uart.o \ + hw_init.o \ dirlist.o \ ini.o \ ianos.o \ diff --git a/bootloader/frontend/fe_emmc_tools.c b/bootloader/frontend/fe_emmc_tools.c new file mode 100644 index 0000000..1ca7d3c --- /dev/null +++ b/bootloader/frontend/fe_emmc_tools.c @@ -0,0 +1,884 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 Rajko Stojadinovic + * Copyright (c) 2018 CTCaer + * + * 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 . + */ + +#include +#include + +#include "fe_emmc_tools.h" +#include "../config/config.h" +#include "../gfx/gfx.h" +#include "../gfx/tui.h" +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../sec/se.h" +#include "../storage/nx_emmc.h" +#include "../storage/sdmmc.h" +#include "../utils/btn.h" +#include "../utils/util.h" + +extern sdmmc_t sd_sdmmc; +extern sdmmc_storage_t sd_storage; +extern FATFS sd_fs; +extern hekate_config h_cfg; +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; + +extern bool sd_mount(); +extern void sd_unmount(); +extern void emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage); + +//TODO: Create more macros (info, header, debug, etc) with different colors and utilize them for consistency. +#define EPRINTF(text) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, 0xFFCCCCCC) +#define EPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, args, 0xFFCCCCCC) +#define WPRINTF(text) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFFDD00, 0xFFCCCCCC) +#define WPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFFDD00, args, 0xFFCCCCCC) + +int _dump_emmc_verify(sdmmc_storage_t *storage, u32 lba_curr, char *outFilename, emmc_part_t *part) +{ + FIL fp; + u32 btn = 0; + u32 prevPct = 200; + int res = 0; + + u8 hashEm[0x20]; + u8 hashSd[0x20]; + + if (f_open(&fp, outFilename, FA_READ) == FR_OK) + { + u32 totalSectorsVer = (u32)((u64)f_size(&fp) >> (u64)9); + + u32 numSectorsPerIter = 0; + if (totalSectorsVer > 0x200000) + numSectorsPerIter = 8192; //4MB Cache + else + numSectorsPerIter = 512; //256KB Cache + + u8 *bufEm = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); + u8 *bufSd = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); + + u32 pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); + tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFF96FF00, 0xFF155500); + + u32 num = 0; + while (totalSectorsVer > 0) + { + num = MIN(totalSectorsVer, numSectorsPerIter); + + if (!sdmmc_storage_read(storage, lba_curr, num, bufEm)) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFailed to read %d blocks (@LBA %08X),\nfrom eMMC!\n\nVerification failed..\n", + num, lba_curr); + + free(bufEm); + free(bufSd); + f_close(&fp); + return 1; + } + if (f_read(&fp, bufSd, num << 9, NULL)) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFailed to read %d blocks (@LBA %08X),\nfrom sd card!\n\nVerification failed..\n", num, lba_curr); + + free(bufEm); + free(bufSd); + f_close(&fp); + return 1; + } + + switch (h_cfg.verification) + { + case 1: + res = memcmp32sparse((u32 *)bufEm, (u32 *)bufSd, num << 9); + break; + case 2: + default: + se_calc_sha256(&hashEm, bufEm, num << 9); + se_calc_sha256(&hashSd, bufSd, num << 9); + res = memcmp(hashEm, hashSd, 0x10); + break; + } + if (res) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nSD card and eMMC data (@LBA %08X),\ndo not match!\n\nVerification failed..\n", lba_curr); + + free(bufEm); + free(bufSd); + f_close(&fp); + return 1; + } + + pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); + if (pct != prevPct) + { + tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFF96FF00, 0xFF155500); + prevPct = pct; + } + + lba_curr += num; + totalSectorsVer -= num; + + btn = btn_wait_timeout(0, BTN_VOL_DOWN | BTN_VOL_UP); + if ((btn & BTN_VOL_DOWN) && (btn & BTN_VOL_UP)) + { + gfx_con.fntsz = 16; + WPRINTF("\n\nThe verification was cancelled!"); + EPRINTF("\nPress any key...\n"); + msleep(1500); + + free(bufEm); + free(bufSd); + f_close(&fp); + + return 0; + } + } + free(bufEm); + free(bufSd); + f_close(&fp); + + tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); + + return 0; + } + else + { + gfx_con.fntsz = 16; + EPRINTF("\nFile not found or could not be loaded.\n\nVerification failed..\n"); + return 1; + } +} + +int _dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part) +{ + static const u32 FAT32_FILESIZE_LIMIT = 0xFFFFFFFF; + static const u32 SECTORS_TO_MIB_COEFF = 11; + + u32 multipartSplitSize = (1u << 31); + u32 totalSectors = part->lba_end - part->lba_start + 1; + u32 currPartIdx = 0; + u32 numSplitParts = 0; + u32 maxSplitParts = 0; + u32 btn = 0; + bool isSmallSdCard = false; + bool partialDumpInProgress = false; + int res = 0; + char *outFilename = sd_path; + u32 sdPathLen = strlen(sd_path); + + FIL partialIdxFp; + char partialIdxFilename[12]; + memcpy(partialIdxFilename, "partial.idx", 12); + + gfx_con.fntsz = 8; + gfx_printf(&gfx_con, "\nSD Card free space: %d MiB, Total backup size %d MiB\n\n", + sd_fs.free_clst * sd_fs.csize >> SECTORS_TO_MIB_COEFF, + totalSectors >> SECTORS_TO_MIB_COEFF); + + // 1GB parts for sd cards 8GB and less. + if ((sd_storage.csd.capacity >> (20 - sd_storage.csd.read_blkbits)) <= 8192) + multipartSplitSize = (1u << 30); + // Maximum parts fitting the free space available. + maxSplitParts = (sd_fs.free_clst * sd_fs.csize) / (multipartSplitSize / 512); + + // Check if the USER partition or the RAW eMMC fits the sd card free space. + if (totalSectors > (sd_fs.free_clst * sd_fs.csize)) + { + isSmallSdCard = true; + + gfx_printf(&gfx_con, "%k\nSD card free space is smaller than total backup size.%k\n", 0xFFFFBA00, 0xFFCCCCCC); + + if (!maxSplitParts) + { + gfx_con.fntsz = 16; + EPRINTF("Not enough free space for Partial Backup."); + + return 0; + } + } + // Check if we are continuing a previous raw eMMC or USER partition backup in progress. + if (f_open(&partialIdxFp, partialIdxFilename, FA_READ) == FR_OK && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) + { + gfx_printf(&gfx_con, "%kFound Partial Backup in progress. Continuing...%k\n\n", 0xFFAEFD14, 0xFFCCCCCC); + + partialDumpInProgress = true; + // Force partial dumping, even if the card is larger. + isSmallSdCard = true; + + f_read(&partialIdxFp, &currPartIdx, 4, NULL); + f_close(&partialIdxFp); + + if (!maxSplitParts) + { + gfx_con.fntsz = 16; + EPRINTF("Not enough free space for Partial Backup."); + + return 0; + } + + // Increase maxSplitParts to accommodate previously backed up parts. + maxSplitParts += currPartIdx; + } + else if (isSmallSdCard) + gfx_printf(&gfx_con, "%kPartial Backup enabled (with %d MiB parts)...%k\n\n", 0xFFFFBA00, multipartSplitSize >> 20, 0xFFCCCCCC); + + // Check if filesystem is FAT32 or the free space is smaller and backup in parts. + if (((sd_fs.fs_type != FS_EXFAT) && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) | isSmallSdCard) + { + u32 multipartSplitSectors = multipartSplitSize / NX_EMMC_BLOCKSIZE; + numSplitParts = (totalSectors + multipartSplitSectors - 1) / multipartSplitSectors; + + outFilename[sdPathLen++] = '.'; + + if (!partialDumpInProgress) + { + outFilename[sdPathLen] = '0'; + if (numSplitParts >= 10) + { + outFilename[sdPathLen + 1] = '0'; + outFilename[sdPathLen + 2] = 0; + } + else + outFilename[sdPathLen + 1] = 0; + } + // Continue from where we left, if Partial Backup in progress. + else + { + if (numSplitParts >= 10 && currPartIdx < 10) + { + outFilename[sdPathLen] = '0'; + itoa(currPartIdx, &outFilename[sdPathLen + 1], 10); + } + else + itoa(currPartIdx, &outFilename[sdPathLen], 10); + } + } + + FIL fp; + gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); + if (!f_open(&fp, outFilename, FA_READ)) + { + f_close(&fp); + gfx_con.fntsz = 16; + + WPRINTF("An existing backup has been detected!"); + WPRINTF("Press POWER to Continue.\nPress VOL to go to the menu.\n"); + msleep(500); + + if (!(btn_wait() & BTN_POWER)) + return 0; + gfx_con.fntsz = 8; + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, gfx_con.savedy, 48); + } + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + gfx_printf(&gfx_con, "Filename: %s\n\n", outFilename); + res = f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE); + if (res) + { + gfx_con.fntsz = 16; + EPRINTFARGS("Error (%d) creating file %s.\n", res, outFilename); + + return 0; + } + + u32 numSectorsPerIter = 0; + if (totalSectors > 0x200000) + numSectorsPerIter = 8192; + else + numSectorsPerIter = 512; + u8 *buf = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); + + u32 lba_curr = part->lba_start; + u32 lbaStartPart = part->lba_start; + u32 bytesWritten = 0; + u32 prevPct = 200; + int retryCount = 0; + + // Continue from where we left, if Partial Backup in progress. + if (partialDumpInProgress) + { + lba_curr += currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE); + totalSectors -= currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE); + lbaStartPart = lba_curr; // Update the start LBA for verification. + } + u64 totalSize = (u64)((u64)totalSectors << 9); + if (!isSmallSdCard && sd_fs.fs_type == FS_EXFAT) + f_lseek(&fp, totalSize); + else + f_lseek(&fp, MIN(totalSize, multipartSplitSize)); + f_lseek(&fp, 0); + + u32 num = 0; + u32 pct = 0; + while (totalSectors > 0) + { + if (numSplitParts != 0 && bytesWritten >= multipartSplitSize) + { + f_close(&fp); + memset(&fp, 0, sizeof(fp)); + currPartIdx++; + + if (h_cfg.verification) + { + // Verify part. + if (_dump_emmc_verify(storage, lbaStartPart, outFilename, part)) + { + EPRINTF("\nPress any key and try again...\n"); + + free(buf); + return 0; + } + } + + if (numSplitParts >= 10 && currPartIdx < 10) + { + outFilename[sdPathLen] = '0'; + itoa(currPartIdx, &outFilename[sdPathLen + 1], 10); + } + else + itoa(currPartIdx, &outFilename[sdPathLen], 10); + + // Always create partial.idx before next part, in case a fatal error occurs. + if (isSmallSdCard) + { + // Create partial backup index file. + if (f_open(&partialIdxFp, partialIdxFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) + { + f_write(&partialIdxFp, &currPartIdx, 4, NULL); + f_close(&partialIdxFp); + } + else + { + gfx_con.fntsz = 16; + EPRINTF("\nError creating partial.idx file.\n"); + + free(buf); + return 0; + } + + // More parts to backup that do not currently fit the sd card free space or fatal error. + if (currPartIdx >= maxSplitParts) + { + gfx_puts(&gfx_con, "\n\n1. Press any key to unmount SD Card.\n\ + 2. Remove SD Card and move files to free space.\n\ + Don\'t move the partial.idx file!\n\ + 3. Re-insert SD Card.\n\ + 4. Select the SAME option again to continue.\n"); + gfx_con.fntsz = 16; + + free(buf); + return 1; + } + } + + // Create next part. + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + gfx_printf(&gfx_con, "Filename: %s\n\n", outFilename); + lbaStartPart = lba_curr; + res = f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE); + if (res) + { + gfx_con.fntsz = 16; + EPRINTFARGS("Error (%d) creating file %s.\n", res, outFilename); + + free(buf); + return 0; + } + bytesWritten = 0; + + totalSize = (u64)((u64)totalSectors << 9); + f_lseek(&fp, MIN(totalSize, multipartSplitSize)); + f_lseek(&fp, 0); + } + + retryCount = 0; + num = MIN(totalSectors, numSectorsPerIter); + while (!sdmmc_storage_read(storage, lba_curr, num, buf)) + { + EPRINTFARGS("Error reading %d blocks @ LBA %08X,\nfrom eMMC (try %d), retrying...", + num, lba_curr, ++retryCount); + + msleep(150); + if (retryCount >= 3) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFailed to read %d blocks @ LBA %08X\nfrom eMMC. Aborting..\n", + num, lba_curr); + EPRINTF("\nPress any key and try again...\n"); + + free(buf); + f_close(&fp); + f_unlink(outFilename); + + return 0; + } + } + res = f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL); + if (res) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFatal error (%d) when writing to SD Card", res); + EPRINTF("\nPress any key and try again...\n"); + + free(buf); + f_close(&fp); + f_unlink(outFilename); + + return 0; + } + pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); + if (pct != prevPct) + { + tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); + prevPct = pct; + } + + lba_curr += num; + totalSectors -= num; + bytesWritten += num * NX_EMMC_BLOCKSIZE; + + // Force a flush after a lot of data if not splitting. + if (numSplitParts == 0 && bytesWritten >= multipartSplitSize) + { + f_sync(&fp); + bytesWritten = 0; + } + + btn = btn_wait_timeout(0, BTN_VOL_DOWN | BTN_VOL_UP); + if ((btn & BTN_VOL_DOWN) && (btn & BTN_VOL_UP)) + { + gfx_con.fntsz = 16; + WPRINTF("\n\nThe backup was cancelled!"); + EPRINTF("\nPress any key...\n"); + msleep(1500); + + free(buf); + f_close(&fp); + f_unlink(outFilename); + + return 0; + } + } + tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFFCCCCCC, 0xFF555555); + + // Backup operation ended successfully. + free(buf); + f_close(&fp); + + if (h_cfg.verification) + { + // Verify last part or single file backup. + if (_dump_emmc_verify(storage, lbaStartPart, outFilename, part)) + { + EPRINTF("\nPress any key and try again...\n"); + + return 0; + } + else + tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFF96FF00, 0xFF155500); + } + + gfx_con.fntsz = 16; + // Remove partial backup index file if no fatal errors occurred. + if (isSmallSdCard) + { + f_unlink(partialIdxFilename); + gfx_printf(&gfx_con, "%k\n\nYou can now join the files\nand get the complete eMMC RAW GPP backup.", 0xFFCCCCCC); + } + gfx_puts(&gfx_con, "\n\n"); + + return 1; +} + +typedef enum +{ + PART_BOOT = (1 << 0), + PART_SYSTEM = (1 << 1), + PART_USER = (1 << 2), + PART_RAW = (1 << 3), + PART_GP_ALL = (1 << 7) +} emmcPartType_t; + +static void _dump_emmc_selected(emmcPartType_t dumpType) +{ + int res = 0; + u32 timer = 0; + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + tui_sbar(&gfx_con, true); + gfx_con_setpos(&gfx_con, 0, 0); + + if (!sd_mount()) + goto out; + + gfx_puts(&gfx_con, "Checking for available free space...\n\n"); + // Get SD Card free space for Partial Backup. + f_getfree("", &sd_fs.free_clst, NULL); + + sdmmc_storage_t storage; + sdmmc_t sdmmc; + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + goto out; + } + + int i = 0; + char sdPath[80]; + // Create Restore folders, if they do not exist. + emmcsn_path_impl(sdPath, "/restore", "", &storage); + emmcsn_path_impl(sdPath, "/restore/partitions", "", &storage); + + timer = get_tmr_s(); + if (dumpType & PART_BOOT) + { + const u32 BOOT_PART_SIZE = storage.ext_csd.boot_mult << 17; + + emmc_part_t bootPart; + memset(&bootPart, 0, sizeof(bootPart)); + bootPart.lba_start = 0; + bootPart.lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1; + for (i = 0; i < 2; i++) + { + memcpy(bootPart.name, "BOOT", 5); + bootPart.name[4] = (u8)('0' + i); + bootPart.name[5] = 0; + + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i, + bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFCCCCCC); + + sdmmc_storage_set_mmc_partition(&storage, i + 1); + + emmcsn_path_impl(sdPath, "", bootPart.name, &storage); + res = _dump_emmc_part(sdPath, &storage, &bootPart); + } + } + + if ((dumpType & PART_SYSTEM) || (dumpType & PART_USER) || (dumpType & PART_RAW)) + { + sdmmc_storage_set_mmc_partition(&storage, 0); + + if ((dumpType & PART_SYSTEM) || (dumpType & PART_USER)) + { + LIST_INIT(gpt); + nx_emmc_gpt_parse(&gpt, &storage); + LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) + { + if ((dumpType & PART_USER) == 0 && !strcmp(part->name, "USER")) + continue; + if ((dumpType & PART_SYSTEM) == 0 && strcmp(part->name, "USER")) + continue; + + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, + part->name, part->lba_start, part->lba_end, 0xFFCCCCCC); + + emmcsn_path_impl(sdPath, "/partitions", part->name, &storage); + res = _dump_emmc_part(sdPath, &storage, part); + // If a part failed, don't continue. + if (!res) + break; + } + nx_emmc_gpt_free(&gpt); + } + + if (dumpType & PART_RAW) + { + // Get GP partition size dynamically. + const u32 RAW_AREA_NUM_SECTORS = storage.sec_cnt; + + emmc_part_t rawPart; + memset(&rawPart, 0, sizeof(rawPart)); + rawPart.lba_start = 0; + rawPart.lba_end = RAW_AREA_NUM_SECTORS - 1; + strcpy(rawPart.name, "rawnand.bin"); + { + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, + rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFCCCCCC); + + emmcsn_path_impl(sdPath, "", rawPart.name, &storage); + res = _dump_emmc_part(sdPath, &storage, &rawPart); + } + } + } + + gfx_putc(&gfx_con, '\n'); + timer = get_tmr_s() - timer; + gfx_printf(&gfx_con, "Time taken: %dm %ds.\n", timer / 60, timer % 60); + sdmmc_storage_end(&storage); + if (res && h_cfg.verification) + gfx_printf(&gfx_con, "\n%kFinished and verified!%k\nPress any key...\n", 0xFF96FF00, 0xFFCCCCCC); + else if (res) + gfx_printf(&gfx_con, "\nFinished! Press any key...\n"); + +out: + sd_unmount(); + btn_wait(); +} + +void dump_emmc_system() { _dump_emmc_selected(PART_SYSTEM); } +void dump_emmc_user() { _dump_emmc_selected(PART_USER); } +void dump_emmc_boot() { _dump_emmc_selected(PART_BOOT); } +void dump_emmc_rawnand() { _dump_emmc_selected(PART_RAW); } + +int _restore_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part) +{ + static const u32 SECTORS_TO_MIB_COEFF = 11; + + u32 totalSectors = part->lba_end - part->lba_start + 1; + u32 lbaStartPart = part->lba_start; + int res = 0; + char *outFilename = sd_path; + + gfx_con.fntsz = 8; + + FIL fp; + gfx_printf(&gfx_con, "\nFilename: %s\n", outFilename); + + res = f_open(&fp, outFilename, FA_READ); + if (res) + { + WPRINTFARGS("Error (%d) while opening backup. Continuing...\n", res); + gfx_con.fntsz = 16; + + return 0; + } + //TODO: Should we keep this check? + else if (((u32)((u64)f_size(&fp) >> (u64)9)) != totalSectors) + { + gfx_con.fntsz = 16; + EPRINTF("Size of the SD Card backup does not match,\neMMC's selected part size.\n"); + f_close(&fp); + + return 0; + } + else + gfx_printf(&gfx_con, "\nTotal restore size: %d MiB.\n\n", ((u32)((u64)f_size(&fp) >> (u64)9)) >> SECTORS_TO_MIB_COEFF); + + u32 numSectorsPerIter = 0; + if (totalSectors > 0x200000) + numSectorsPerIter = 8192; //4MB Cache + else + numSectorsPerIter = 512; //256KB Cache + + u8 *buf = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); + + u32 lba_curr = part->lba_start; + u32 bytesWritten = 0; + u32 prevPct = 200; + int retryCount = 0; + + u32 num = 0; + u32 pct = 0; + while (totalSectors > 0) + { + retryCount = 0; + num = MIN(totalSectors, numSectorsPerIter); + + res = f_read(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL); + if (res) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFatal error (%d) when reading from SD Card", res); + EPRINTF("\nYour device may be in an inoperative state!\n\nPress any key and try again now...\n"); + + free(buf); + f_close(&fp); + return 0; + } + while (!sdmmc_storage_write(storage, lba_curr, num, buf)) + { + EPRINTFARGS("Error writing %d blocks @ LBA %08X\nto eMMC (try %d), retrying...", + num, lba_curr, ++retryCount); + + msleep(150); + if (retryCount >= 3) + { + gfx_con.fntsz = 16; + EPRINTFARGS("\nFailed to write %d blocks @ LBA %08X\nfrom eMMC. Aborting..\n", + num, lba_curr); + EPRINTF("\nYour device may be in an inoperative state!\n\nPress any key and try again...\n"); + + free(buf); + f_close(&fp); + return 0; + } + } + pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); + if (pct != prevPct) + { + tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); + prevPct = pct; + } + + lba_curr += num; + totalSectors -= num; + bytesWritten += num * NX_EMMC_BLOCKSIZE; + } + tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFFCCCCCC, 0xFF555555); + + // Restore operation ended successfully. + free(buf); + f_close(&fp); + + if (h_cfg.verification) + { + // Verify restored data. + if (_dump_emmc_verify(storage, lbaStartPart, outFilename, part)) + { + EPRINTF("\nPress any key and try again...\n"); + + return 0; + } + else + tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFF96FF00, 0xFF155500); + } + + gfx_con.fntsz = 16; + gfx_puts(&gfx_con, "\n\n"); + + return 1; +} + +static void _restore_emmc_selected(emmcPartType_t restoreType) +{ + int res = 0; + u32 timer = 0; + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + tui_sbar(&gfx_con, true); + gfx_con_setpos(&gfx_con, 0, 0); + + gfx_printf(&gfx_con, "%kThis is a dangerous operation\nand may render your device inoperative!\n\n", 0xFFFFDD00); + gfx_printf(&gfx_con, "Are you really sure?\n\n%k", 0xFFCCCCCC); + if ((restoreType & PART_BOOT) || (restoreType & PART_GP_ALL)) + { + gfx_puts(&gfx_con, "The mode you selected will only restore\nthe "); + if (restoreType & PART_BOOT) + gfx_puts(&gfx_con, "boot "); + gfx_puts(&gfx_con, "partitions that it can find.\n"); + gfx_puts(&gfx_con, "If it is not found, it will be skipped\nand continue with the next.\n\n"); + } + gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); + + u8 value = 10; + while (value > 0) + { + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + gfx_printf(&gfx_con, "%kWait... (%ds) %k", 0xFF888888, value, 0xFFCCCCCC); + msleep(1000); + value--; + } + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + + gfx_puts(&gfx_con, "Press POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); + + u32 btn = btn_wait(); + if (!(btn & BTN_POWER)) + goto out; + + if (!sd_mount()) + goto out; + + sdmmc_storage_t storage; + sdmmc_t sdmmc; + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + goto out; + } + + int i = 0; + char sdPath[80]; + + timer = get_tmr_s(); + if (restoreType & PART_BOOT) + { + const u32 BOOT_PART_SIZE = storage.ext_csd.boot_mult << 17; + + emmc_part_t bootPart; + memset(&bootPart, 0, sizeof(bootPart)); + bootPart.lba_start = 0; + bootPart.lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1; + for (i = 0; i < 2; i++) + { + memcpy(bootPart.name, "BOOT", 4); + bootPart.name[4] = (u8)('0' + i); + bootPart.name[5] = 0; + + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i, + bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFCCCCCC); + + sdmmc_storage_set_mmc_partition(&storage, i + 1); + + emmcsn_path_impl(sdPath, "/restore", bootPart.name, &storage); + res = _restore_emmc_part(sdPath, &storage, &bootPart); + } + } + + if (restoreType & PART_GP_ALL) + { + sdmmc_storage_set_mmc_partition(&storage, 0); + + LIST_INIT(gpt); + nx_emmc_gpt_parse(&gpt, &storage); + LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) + { + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, + part->name, part->lba_start, part->lba_end, 0xFFCCCCCC); + + emmcsn_path_impl(sdPath, "/restore/partitions/", part->name, &storage); + res = _restore_emmc_part(sdPath, &storage, part); + } + nx_emmc_gpt_free(&gpt); + } + + if (restoreType & PART_RAW) + { + // Get GP partition size dynamically. + const u32 RAW_AREA_NUM_SECTORS = storage.sec_cnt; + + emmc_part_t rawPart; + memset(&rawPart, 0, sizeof(rawPart)); + rawPart.lba_start = 0; + rawPart.lba_end = RAW_AREA_NUM_SECTORS - 1; + strcpy(rawPart.name, "rawnand.bin"); + { + gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, + rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFCCCCCC); + + emmcsn_path_impl(sdPath, "/restore", rawPart.name, &storage); + res = _restore_emmc_part(sdPath, &storage, &rawPart); + } + } + + gfx_putc(&gfx_con, '\n'); + timer = get_tmr_s() - timer; + gfx_printf(&gfx_con, "Time taken: %dm %ds.\n", timer / 60, timer % 60); + sdmmc_storage_end(&storage); + if (res && h_cfg.verification) + gfx_printf(&gfx_con, "\n%kFinished and verified!%k\nPress any key...\n", 0xFF96FF00, 0xFFCCCCCC); + else if (res) + gfx_printf(&gfx_con, "\nFinished! Press any key...\n"); + +out: + sd_unmount(); + btn_wait(); +} + +void restore_emmc_boot() { _restore_emmc_selected(PART_BOOT); } +void restore_emmc_rawnand() { _restore_emmc_selected(PART_RAW); } +void restore_emmc_gpp_parts() { _restore_emmc_selected(PART_GP_ALL); } diff --git a/bootloader/frontend/fe_emmc_tools.h b/bootloader/frontend/fe_emmc_tools.h new file mode 100644 index 0000000..059a03a --- /dev/null +++ b/bootloader/frontend/fe_emmc_tools.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 Rajko Stojadinovic + * Copyright (c) 2018 CTCaer + * + * 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 . + */ + +#ifndef _FE_EMMC_TOOLS_H_ +#define _FE_EMMC_TOOLS_H_ + +void dump_emmc_system(); +void dump_emmc_user(); +void dump_emmc_boot(); +void dump_emmc_rawnand(); + +void restore_emmc_boot(); +void restore_emmc_rawnand(); +void restore_emmc_gpp_parts(); + +#endif diff --git a/bootloader/frontend/fe_info.c b/bootloader/frontend/fe_info.c new file mode 100644 index 0000000..aa1b639 --- /dev/null +++ b/bootloader/frontend/fe_info.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * 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 . + */ + +#include + +#include "fe_info.h" +#include "../gfx/gfx.h" +#include "../hos/hos.h" +#include "../hos/pkg1.h" +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../power/bq24193.h" +#include "../power/max17050.h" +#include "../sec/tsec.h" +#include "../soc/fuse.h" +#include "../soc/i2c.h" +#include "../soc/kfuse.h" +#include "../soc/t210.h" +#include "../storage/mmc.h" +#include "../storage/nx_emmc.h" +#include "../storage/sdmmc.h" +#include "../utils/btn.h" +#include "../utils/util.h" + +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; +extern sdmmc_storage_t sd_storage; +extern FATFS sd_fs; + +extern bool sd_mount(); +extern void sd_unmount(); +extern int sd_save_to_file(void *buf, u32 size, const char *filename); +extern void emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage); + +//TODO: Create more macros (info, header, debug, etc) with different colors and utilize them for consistency. +#define EPRINTF(text) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, 0xFFCCCCCC) +#define EPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, args, 0xFFCCCCCC) +#define WPRINTF(text) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFFDD00, 0xFFCCCCCC) +#define WPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFFDD00, args, 0xFFCCCCCC) + +void print_fuseinfo() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + u32 burntFuses = 0; + for (u32 i = 0; i < 32; i++) + { + if ((fuse_read_odm(7) >> i) & 1) + burntFuses++; + } + + gfx_printf(&gfx_con, "\nSKU: %X - ", FUSE(FUSE_SKU_INFO)); + switch (fuse_read_odm(4) & 3) + { + case 0: + gfx_printf(&gfx_con, "Retail\n"); + break; + case 3: + gfx_printf(&gfx_con, "Dev\n"); + break; + } + gfx_printf(&gfx_con, "Sdram ID: %d\n", (fuse_read_odm(4) >> 3) & 0x1F); + gfx_printf(&gfx_con, "Burnt fuses: %d\n", burntFuses); + gfx_printf(&gfx_con, "Secure key: %08X%08X%08X%08X\n\n\n", + 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))); + + gfx_printf(&gfx_con, "%k(Unlocked) fuse cache:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC); + gfx_hexdump(&gfx_con, 0x7000F900, (u8 *)0x7000F900, 0x2FC); + + gfx_puts(&gfx_con, "Press POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + u32 btn = btn_wait(); + if (btn & BTN_POWER) + { + if (sd_mount()) + { + char path[64]; + emmcsn_path_impl(path, "/dumps", "fuses.bin", NULL); + if (!sd_save_to_file((u8 *)0x7000F900, 0x2FC, path)) + gfx_puts(&gfx_con, "\nDone!\n"); + sd_unmount(); + } + + btn_wait(); + } +} + +void print_kfuseinfo() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + gfx_printf(&gfx_con, "%kKFuse contents:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC); + u32 buf[KFUSE_NUM_WORDS]; + if (!kfuse_read(buf)) + EPRINTF("CRC fail."); + else + gfx_hexdump(&gfx_con, 0, (u8 *)buf, KFUSE_NUM_WORDS * 4); + + gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + 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)) + gfx_puts(&gfx_con, "\nDone!\n"); + sd_unmount(); + } + + btn_wait(); + } +} + +void print_mmc_info() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + static const u32 SECTORS_TO_MIB_COEFF = 11; + + sdmmc_storage_t storage; + sdmmc_t sdmmc; + + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + goto out; + } + else + { + u16 card_type; + u32 speed = 0; + + gfx_printf(&gfx_con, "%kCID:%k\n", 0xFF00DDFF, 0xFFCCCCCC); + switch (storage.csd.mmca_vsn) + { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + gfx_printf(&gfx_con, + " Vendor ID: %03X\n" + " Model: %c%c%c%c%c%c%c\n" + " HW rev: %X\n" + " FW rev: %X\n" + " S/N: %03X\n" + " Month/Year: %02d/%04d\n\n", + storage.cid.manfid, + storage.cid.prod_name[0], storage.cid.prod_name[1], storage.cid.prod_name[2], + storage.cid.prod_name[3], storage.cid.prod_name[4], storage.cid.prod_name[5], + storage.cid.prod_name[6], storage.cid.hwrev, storage.cid.fwrev, + storage.cid.serial, storage.cid.month, storage.cid.year); + break; + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + gfx_printf(&gfx_con, + " Vendor ID: %X\n" + " Card/BGA: %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", + storage.cid.manfid, storage.cid.card_bga, storage.cid.oemid, + storage.cid.prod_name[0], storage.cid.prod_name[1], storage.cid.prod_name[2], + storage.cid.prod_name[3], storage.cid.prod_name[4], storage.cid.prod_name[5], + storage.cid.prv, storage.cid.serial, storage.cid.month, storage.cid.year); + break; + default: + EPRINTFARGS("eMMC has unknown MMCA version %d", storage.csd.mmca_vsn); + break; + } + + if (storage.csd.structure == 0) + EPRINTF("Unknown CSD structure."); + else + { + gfx_printf(&gfx_con, "%kExtended CSD V1.%d:%k\n", + 0xFF00DDFF, storage.ext_csd.ext_struct, 0xFFCCCCCC); + card_type = storage.ext_csd.card_type; + u8 card_type_support[96]; + u8 pos_type = 0; + card_type_support[0] = 0; + if (card_type & EXT_CSD_CARD_TYPE_HS_26) + { + memcpy(card_type_support, "HS26", 4); + speed = (26 << 16) | 26; + pos_type += 4; + } + if (card_type & EXT_CSD_CARD_TYPE_HS_52) + { + memcpy(card_type_support + pos_type, ", HS52", 6); + speed = (52 << 16) | 52; + pos_type += 6; + } + if (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) + { + memcpy(card_type_support + pos_type, ", DDR52_1.8V", 12); + speed = (52 << 16) | 104; + pos_type += 12; + } + if (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) + { + memcpy(card_type_support + pos_type, ", HS200_1.8V", 12); + speed = (200 << 16) | 200; + pos_type += 12; + } + if (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) + { + memcpy(card_type_support + pos_type, ", HS400_1.8V", 12); + speed = (200 << 16) | 400; + pos_type += 12; + } + card_type_support[pos_type] = 0; + + gfx_printf(&gfx_con, + " 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: ", + storage.csd.mmca_vsn, storage.ext_csd.rev, storage.ext_csd.dev_version, storage.csd.cmdclass, + storage.csd.capacity == (4096 * 512) ? "High" : "Low", speed & 0xFFFF, (speed >> 16) & 0xFFFF, + storage.csd.busspeed); + gfx_con.fntsz = 8; + gfx_printf(&gfx_con, "%s", card_type_support); + gfx_con.fntsz = 16; + gfx_printf(&gfx_con, "\n\n", card_type_support); + + u32 boot_size = storage.ext_csd.boot_mult << 17; + u32 rpmb_size = storage.ext_csd.rpmb_mult << 17; + gfx_printf(&gfx_con, "%keMMC Partitions:%k\n", 0xFF00DDFF, 0xFFCCCCCC); + gfx_printf(&gfx_con, " 1: %kBOOT0 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, + boot_size / 1024, boot_size / 1024 / 512); + gfx_put_small_sep(&gfx_con); + gfx_printf(&gfx_con, " 2: %kBOOT1 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, + boot_size / 1024, boot_size / 1024 / 512); + gfx_put_small_sep(&gfx_con); + gfx_printf(&gfx_con, " 3: %kRPMB %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, + rpmb_size / 1024, rpmb_size / 1024 / 512); + gfx_put_small_sep(&gfx_con); + gfx_printf(&gfx_con, " 0: %kGPP (USER) %k\n Size: %5d MiB (LBA Sectors: 0x%07X)\n\n", 0xFF96FF00, 0xFFCCCCCC, + storage.sec_cnt >> SECTORS_TO_MIB_COEFF, storage.sec_cnt); + gfx_put_small_sep(&gfx_con); + gfx_printf(&gfx_con, "%kGPP (eMMC USER) partition table:%k\n", 0xFF00DDFF, 0xFFCCCCCC); + + sdmmc_storage_set_mmc_partition(&storage, 0); + LIST_INIT(gpt); + nx_emmc_gpt_parse(&gpt, &storage); + int gpp_idx = 0; + LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) + { + gfx_printf(&gfx_con, " %02d: %k%s%k\n Size: % 5d MiB (LBA Sectors 0x%07X)\n LBA Range: %08X-%08X\n", + 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); + gfx_put_small_sep(&gfx_con); + } + nx_emmc_gpt_free(&gpt); + } + } + +out: + sdmmc_storage_end(&storage); + + btn_wait(); +} + +void print_sdcard_info() +{ + static const u32 SECTORS_TO_MIB_COEFF = 11; + + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + if (sd_mount()) + { + u32 capacity; + + gfx_printf(&gfx_con, "%kCard IDentification:%k\n", 0xFF00DDFF, 0xFFCCCCCC); + gfx_printf(&gfx_con, + " 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); + + gfx_printf(&gfx_con, "%kCard-Specific Data V%d.0:%k\n", 0xFF00DDFF, sd_storage.csd.structure + 1, 0xFFCCCCCC); + capacity = sd_storage.csd.capacity >> (20 - sd_storage.csd.read_blkbits); + gfx_printf(&gfx_con, + " 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" + " Write Protect: %d\n\n", + sd_storage.csd.cmdclass, capacity, + 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, + sd_storage.ssr.app_class, sd_storage.csd.write_protect); + + gfx_puts(&gfx_con, "Acquiring FAT volume info...\n\n"); + f_getfree("", &sd_fs.free_clst, NULL); + gfx_printf(&gfx_con, "%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); + sd_unmount(); + } + + btn_wait(); +} + +void print_tsec_key() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + sdmmc_storage_t storage; + sdmmc_t sdmmc; + + sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); + + // Read package1. + u8 *pkg1 = (u8 *)malloc(0x40000); + sdmmc_storage_set_mmc_partition(&storage, 1); + sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); + sdmmc_storage_end(&storage); + const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); + if (!pkg1_id) + { + EPRINTFARGS("Unknown package1 version for reading\nTSEC firmware (= '%s').", + (char *)pkg1 + 0x10); + goto out_wait; + } + + u8 keys[0x10 * 3]; + for (u32 i = 1; i <= 3; i++) + { + int res = tsec_query(keys + ((i - 1) * 0x10), i, pkg1 + pkg1_id->tsec_off); + + gfx_printf(&gfx_con, "%kTSEC key %d: %k", 0xFF00DDFF, i, 0xFFCCCCCC); + if (res >= 0) + { + for (u32 j = 0; j < 0x10; j++) + gfx_printf(&gfx_con, "%02X", keys[((i - 1) * 0x10) + j]); + } + else + EPRINTFARGS("ERROR %X", res); + gfx_putc(&gfx_con, '\n'); + } + + gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + u32 btn = btn_wait(); + if (btn & BTN_POWER) + { + if (sd_mount()) + { + char path[64]; + emmcsn_path_impl(path, "/dumps", "tsec_keys.bin", NULL); + if (!sd_save_to_file(keys, 0x10 * 3, path)) + gfx_puts(&gfx_con, "\nDone!\n"); + sd_unmount(); + } + } + else + goto out; + +out_wait: + btn_wait(); + +out: + free(pkg1); +} + +void print_fuel_gauge_info() +{ + int value = 0; + + gfx_printf(&gfx_con, "%kFuel Gauge IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); + + max17050_get_property(MAX17050_Age, &value); + gfx_printf(&gfx_con, "Age: %3d%\n", value); + + max17050_get_property(MAX17050_RepSOC, &value); + gfx_printf(&gfx_con, "Capacity now: %3d%\n", value >> 8); + + max17050_get_property(MAX17050_RepCap, &value); + gfx_printf(&gfx_con, "Capacity now: %4d mAh\n", value); + + max17050_get_property(MAX17050_FullCAP, &value); + gfx_printf(&gfx_con, "Capacity full: %4d mAh\n", value); + + max17050_get_property(MAX17050_DesignCap, &value); + gfx_printf(&gfx_con, "Capacity (design): %4d mAh\n", value); + + max17050_get_property(MAX17050_Current, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Current now: %d mA\n", value / 1000); + else + gfx_printf(&gfx_con, "Current now: -%d mA\n", ~value / 1000); + + max17050_get_property(MAX17050_AvgCurrent, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Current average: %d mA\n", value / 1000); + else + gfx_printf(&gfx_con, "Current average: -%d mA\n", ~value / 1000); + + max17050_get_property(MAX17050_VCELL, &value); + gfx_printf(&gfx_con, "Voltage now: %4d mV\n", value); + + max17050_get_property(MAX17050_OCVInternal, &value); + gfx_printf(&gfx_con, "Voltage open-circuit: %4d mV\n", value); + + max17050_get_property(MAX17050_MinVolt, &value); + gfx_printf(&gfx_con, "Min voltage reached: %4d mV\n", value); + + max17050_get_property(MAX17050_MaxVolt, &value); + gfx_printf(&gfx_con, "Max voltage reached: %4d mV\n", value); + + max17050_get_property(MAX17050_V_empty, &value); + gfx_printf(&gfx_con, "Empty voltage (design): %4d mV\n", value); + + max17050_get_property(MAX17050_TEMP, &value); + if (value >= 0) + gfx_printf(&gfx_con, "Battery temperature: %d.%d oC\n", value / 10, value % 10); + else + gfx_printf(&gfx_con, "Battery temperature: -%d.%d oC\n", ~value / 10, (~value) % 10); +} + +void print_battery_charger_info() +{ + int value = 0; + + gfx_printf(&gfx_con, "%k\n\nBattery Charger IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); + + bq24193_get_property(BQ24193_InputVoltageLimit, &value); + gfx_printf(&gfx_con, "Input voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_InputCurrentLimit, &value); + gfx_printf(&gfx_con, "Input current limit: %4d mA\n", value); + + bq24193_get_property(BQ24193_SystemMinimumVoltage, &value); + gfx_printf(&gfx_con, "Min voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_FastChargeCurrentLimit, &value); + gfx_printf(&gfx_con, "Fast charge current limit: %4d mA\n", value); + + bq24193_get_property(BQ24193_ChargeVoltageLimit, &value); + gfx_printf(&gfx_con, "Charge voltage limit: %4d mV\n", value); + + bq24193_get_property(BQ24193_ChargeStatus, &value); + gfx_printf(&gfx_con, "Charge status: "); + switch (value) + { + case 0: + gfx_printf(&gfx_con, "Not charging\n"); + break; + case 1: + gfx_printf(&gfx_con, "Pre-charging\n"); + break; + case 2: + gfx_printf(&gfx_con, "Fast charging\n"); + break; + case 3: + gfx_printf(&gfx_con, "Charge terminated\n"); + break; + default: + gfx_printf(&gfx_con, "Unknown (%d)\n", value); + break; + } + bq24193_get_property(BQ24193_TempStatus, &value); + gfx_printf(&gfx_con, "Temperature status: "); + switch (value) + { + case 0: + gfx_printf(&gfx_con, "Normal\n"); + break; + case 2: + gfx_printf(&gfx_con, "Warm\n"); + break; + case 3: + gfx_printf(&gfx_con, "Cool\n"); + break; + case 5: + gfx_printf(&gfx_con, "Cold\n"); + break; + case 6: + gfx_printf(&gfx_con, "Hot\n"); + break; + default: + gfx_printf(&gfx_con, "Unknown (%d)\n", value); + break; + } +} + +void print_battery_info() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + print_fuel_gauge_info(); + + print_battery_charger_info(); + + u8 *buf = (u8 *)malloc(0x100 * 2); + + gfx_printf(&gfx_con, "%k\n\nBattery Fuel Gauge Registers:\n%k", 0xFF00DDFF, 0xFFCCCCCC); + + for (int i = 0; i < 0x200; i += 2) + { + i2c_recv_buf_small(buf + i, 2, I2C_1, 0x36, i >> 1); + usleep(2500); + } + + gfx_hexdump(&gfx_con, 0, (u8 *)buf, 0x200); + + gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + 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 + gfx_puts(&gfx_con, "\nDone!\n"); + sd_unmount(); + } + + btn_wait(); + } + free(buf); +} + +void _ipatch_process(u32 offset, u32 value) +{ + gfx_printf(&gfx_con, "%8x %8x", BOOTROM_BASE + offset, value); + u8 lo = value & 0xff; + switch (value >> 8) + { + case 0xdf: + gfx_printf(&gfx_con, " svc #0x%02x", lo); + break; + case 0x20: + gfx_printf(&gfx_con, " movs r0, #0x%02x", lo); + break; + } + gfx_puts(&gfx_con, "\n"); +} + +void bootrom_ipatches_info() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + static const u32 BOOTROM_SIZE = 0x18000; + + u32 res = fuse_read_ipatch(_ipatch_process); + if (res != 0) + EPRINTFARGS("Failed to read ipatches. Error: %d", res); + + gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); + + 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)) + gfx_puts(&gfx_con, "\nevp_thunks.bin saved!\n"); + } + else + EPRINTFARGS("Failed to read evp_thunks. Error: %d", res); + + u32 words[0x100]; + read_raw_ipatch_fuses(words); + emmcsn_path_impl(path, "/dumps", "ipatches.bin", NULL); + if (!sd_save_to_file((u8 *)words, sizeof(words), path)) + gfx_puts(&gfx_con, "\nipatches.bin saved!\n"); + + emmcsn_path_impl(path, "/dumps", "bootrom_patched.bin", NULL); + if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) + gfx_puts(&gfx_con, "\nbootrom_patched.bin saved!\n"); + + u8 ipatch_backup[13]; + memcpy(ipatch_backup, (void *) IPATCH_BASE, 13); + memset((void*)IPATCH_BASE, 0, 13); + + emmcsn_path_impl(path, "/dumps", "bootrom_unpatched.bin", NULL); + if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) + gfx_puts(&gfx_con, "\nbootrom_unpatched.bin saved!\n"); + + memcpy((void*)IPATCH_BASE, ipatch_backup, 13); + + sd_unmount(); + } + + btn_wait(); + } +} diff --git a/bootloader/frontend/fe_info.h b/bootloader/frontend/fe_info.h new file mode 100644 index 0000000..64eb83f --- /dev/null +++ b/bootloader/frontend/fe_info.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * 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 . + */ + +#ifndef _FE_INFO_H_ +#define _FE_INFO_H_ + +void print_fuseinfo(); +void print_kfuseinfo(); +void print_mmc_info(); +void print_sdcard_info(); +void print_tsec_key(); +void print_fuel_gauge_info(); +void print_battery_charger_info(); +void print_battery_info(); +void bootrom_ipatches_info(); + +#endif diff --git a/bootloader/frontend/fe_tools.c b/bootloader/frontend/fe_tools.c new file mode 100644 index 0000000..24edab2 --- /dev/null +++ b/bootloader/frontend/fe_tools.c @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * Copyright (c) 2018 Reisyukaku + * + * 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 . + */ + +#include +#include + +#include "fe_tools.h" +#include "../config/config.h" +#include "../gfx/gfx.h" +#include "../gfx/tui.h" +#include "../hos/hos.h" +#include "../hos/pkg1.h" +#include "../hos/pkg2.h" +#include "../libs/fatfs/ff.h" +#include "../mem/heap.h" +#include "../power/max7762x.h" +#include "../storage/nx_emmc.h" +#include "../storage/sdmmc.h" +#include "../utils/btn.h" +#include "../utils/util.h" + +extern hekate_config h_cfg; +extern gfx_ctxt_t gfx_ctxt; +extern gfx_con_t gfx_con; +extern sdmmc_storage_t sd_storage; + +extern bool sd_mount(); +extern void sd_unmount(); +extern int sd_save_to_file(void *buf, u32 size, const char *filename); +extern void emmcsn_path_impl(char *path, char *sub_dir, char *filename, sdmmc_storage_t *storage); + +//TODO: Create more macros (info, header, debug, etc) with different colors and utilize them for consistency. +#define EPRINTF(text) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, 0xFFCCCCCC) +#define EPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFFFF0000, args, 0xFFCCCCCC) + +void dump_packages12() +{ + if (!sd_mount()) + return; + + u8 *pkg1 = (u8 *)calloc(1, 0x40000); + u8 *warmboot = (u8 *)calloc(1, 0x40000); + u8 *secmon = (u8 *)calloc(1, 0x40000); + u8 *loader = (u8 *)calloc(1, 0x40000); + u8 *pkg2 = NULL; + + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + sdmmc_storage_t storage; + sdmmc_t sdmmc; + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + goto out_free; + } + sdmmc_storage_set_mmc_partition(&storage, 1); + + // Read package1. + sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); + const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); + const pk11_hdr_t *hdr = (pk11_hdr_t *)(pkg1 + pkg1_id->pkg11_off + 0x20); + if (!pkg1_id) + { + gfx_con.fntsz = 8; + EPRINTFARGS("Unknown package1 version for reading\nTSEC firmware (= '%s').", (char *)pkg1 + 0x10); + goto out_free; + } + + if (!h_cfg.se_keygen_done) + { + // Read keyblob. + u8 *keyblob = (u8 *)calloc(NX_EMMC_BLOCKSIZE, 1); + sdmmc_storage_read(&storage, 0x180000 / NX_EMMC_BLOCKSIZE + pkg1_id->kb, 1, keyblob); + + // Decrypt. + keygen(keyblob, pkg1_id->kb, (u8 *)pkg1 + pkg1_id->tsec_off); + + h_cfg.se_keygen_done = 1; + free(keyblob); + } + pkg1_decrypt(pkg1_id, pkg1); + + pkg1_unpack(warmboot, secmon, loader, pkg1_id, pkg1); + + // Display info. + gfx_printf(&gfx_con, "%kNX Bootloader size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->ldr_size); + + gfx_printf(&gfx_con, "%kSecure monitor addr: %k0x%05X\n", 0xFFC7EA46, 0xFFCCCCCC, pkg1_id->secmon_base); + gfx_printf(&gfx_con, "%kSecure monitor size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->sm_size); + + gfx_printf(&gfx_con, "%kWarmboot addr: %k0x%05X\n", 0xFFC7EA46, 0xFFCCCCCC, pkg1_id->warmboot_base); + gfx_printf(&gfx_con, "%kWarmboot size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->wb_size); + + char path[64]; + // Dump package1.1. + emmcsn_path_impl(path, "/pkg1", "pkg1_decr.bin", &storage); + if (sd_save_to_file(pkg1, 0x40000, path)) + goto out_free; + gfx_puts(&gfx_con, "\nFull package1 dumped to pkg1_decr.bin\n"); + + // Dump nxbootloader. + emmcsn_path_impl(path, "/pkg1", "nxloader.bin", &storage); + if (sd_save_to_file(loader, hdr->ldr_size, path)) + goto out_free; + gfx_puts(&gfx_con, "NX Bootloader dumped to nxloader.bin\n"); + + // Dump secmon. + emmcsn_path_impl(path, "/pkg1", "secmon.bin", &storage); + if (sd_save_to_file(secmon, hdr->sm_size, path)) + goto out_free; + gfx_puts(&gfx_con, "Secure Monitor dumped to secmon.bin\n"); + + // Dump warmboot. + emmcsn_path_impl(path, "/pkg1", "warmboot.bin", &storage); + if (sd_save_to_file(warmboot, hdr->wb_size, path)) + goto out_free; + gfx_puts(&gfx_con, "Warmboot dumped to warmboot.bin\n\n\n"); + + // Dump package2.1. + sdmmc_storage_set_mmc_partition(&storage, 0); + // Parse eMMC GPT. + LIST_INIT(gpt); + nx_emmc_gpt_parse(&gpt, &storage); + // Find package2 partition. + emmc_part_t *pkg2_part = nx_emmc_part_find(&gpt, "BCPKG2-1-Normal-Main"); + if (!pkg2_part) + goto out; + + // Read in package2 header and get package2 real size. + u8 *tmp = (u8 *)malloc(NX_EMMC_BLOCKSIZE); + nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, 1, tmp); + u32 *hdr_pkg2_raw = (u32 *)(tmp + 0x100); + u32 pkg2_size = hdr_pkg2_raw[0] ^ hdr_pkg2_raw[2] ^ hdr_pkg2_raw[3]; + free(tmp); + // Read in package2. + u32 pkg2_size_aligned = ALIGN(pkg2_size, NX_EMMC_BLOCKSIZE); + pkg2 = malloc(pkg2_size_aligned); + nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, + pkg2_size_aligned / NX_EMMC_BLOCKSIZE, pkg2); + // Decrypt package2 and parse KIP1 blobs in INI1 section. + pkg2_hdr_t *pkg2_hdr = pkg2_decrypt(pkg2); + + // Display info. + u32 kernel_crc32 = crc32c(pkg2_hdr->data, pkg2_hdr->sec_size[PKG2_SEC_KERNEL]); + gfx_printf(&gfx_con, "\n%kKernel CRC32C: %k0x%08X\n\n", 0xFFC7EA46, 0xFFCCCCCC, kernel_crc32); + gfx_printf(&gfx_con, "%kKernel size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, pkg2_hdr->sec_size[PKG2_SEC_KERNEL]); + gfx_printf(&gfx_con, "%kINI1 size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, pkg2_hdr->sec_size[PKG2_SEC_INI1]); + + // Dump pkg2.1. + emmcsn_path_impl(path, "/pkg2", "pkg2_decr.bin", &storage); + if (sd_save_to_file(pkg2, pkg2_hdr->sec_size[PKG2_SEC_KERNEL] + pkg2_hdr->sec_size[PKG2_SEC_INI1], path)) + goto out; + gfx_puts(&gfx_con, "\nFull package2 dumped to pkg2_decr.bin\n"); + + // Dump kernel. + emmcsn_path_impl(path, "/pkg2", "kernel.bin", &storage); + if (sd_save_to_file(pkg2_hdr->data, pkg2_hdr->sec_size[PKG2_SEC_KERNEL], path)) + goto out; + gfx_puts(&gfx_con, "Kernel dumped to kernel.bin\n"); + + // Dump INI1. + emmcsn_path_impl(path, "/pkg2", "ini1.bin", &storage); + if (sd_save_to_file(pkg2_hdr->data + pkg2_hdr->sec_size[PKG2_SEC_KERNEL], + pkg2_hdr->sec_size[PKG2_SEC_INI1], path)) + goto out; + gfx_puts(&gfx_con, "INI1 kip1 package dumped to ini1.bin\n"); + + gfx_puts(&gfx_con, "\nDone. Press any key...\n"); + +out: + nx_emmc_gpt_free(&gpt); +out_free: + free(pkg1); + free(secmon); + free(warmboot); + free(loader); + free(pkg2); + sdmmc_storage_end(&storage); + sd_unmount(); + + btn_wait(); +} + +void _toggle_autorcm(bool enable) +{ + sdmmc_storage_t storage; + sdmmc_t sdmmc; + + u8 randomXor = 0; + + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + goto out; + } + + u8 *tempbuf = (u8 *)malloc(0x200); + sdmmc_storage_set_mmc_partition(&storage, 1); + + int i, sect = 0; + for (i = 0; i < 4; i++) + { + sect = (0x200 + (0x4000 * i)) / NX_EMMC_BLOCKSIZE; + sdmmc_storage_read(&storage, sect, 1, tempbuf); + + if (enable) + { + do + { + randomXor = get_tmr_us() & 0xFF; // Bricmii style of bricking. + } while (!randomXor); // Avoid the lottery. + + tempbuf[0x10] ^= randomXor; + } + else + tempbuf[0x10] = 0xF7; + sdmmc_storage_write(&storage, sect, 1, tempbuf); + } + + free(tempbuf); + sdmmc_storage_end(&storage); + + if (enable) + gfx_printf(&gfx_con, "%kAutoRCM mode enabled!%k", 0xFFFFBA00, 0xFFCCCCCC); + else + gfx_printf(&gfx_con, "%kAutoRCM mode disabled!%k", 0xFF96FF00, 0xFFCCCCCC); + gfx_printf(&gfx_con, "\n\nPress any key...\n"); + +out: + btn_wait(); +} + +void _enable_autorcm() { _toggle_autorcm(true); } +void _disable_autorcm() { _toggle_autorcm(false); } + +void menu_autorcm() +{ + gfx_clear_grey(&gfx_ctxt, 0x1B); + gfx_con_setpos(&gfx_con, 0, 0); + + // Do a simple check on the main BCT. + sdmmc_storage_t storage; + sdmmc_t sdmmc; + bool disabled = true; + + if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) + { + EPRINTF("Failed to init eMMC."); + btn_wait(); + + return; + } + + u8 *tempbuf = (u8 *)malloc(0x200); + sdmmc_storage_set_mmc_partition(&storage, 1); + sdmmc_storage_read(&storage, 0x200 / NX_EMMC_BLOCKSIZE, 1, tempbuf); + + if (tempbuf[0x10] != 0xF7) + disabled = false; + + free(tempbuf); + sdmmc_storage_end(&storage); + + // Create AutoRCM menu. + ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * 6); + + ments[0].type = MENT_BACK; + ments[0].caption = "Back"; + + ments[1].type = MENT_CHGLINE; + + ments[2].type = MENT_CAPTION; + ments[3].type = MENT_CHGLINE; + if (disabled) + { + ments[2].caption = "Status: Disabled!"; + ments[2].color = 0xFF96FF00; + ments[4].caption = "Enable AutoRCM"; + ments[4].handler = _enable_autorcm; + } + else + { + ments[2].caption = "Status: Enabled!"; + ments[2].color = 0xFFFFBA00; + ments[4].caption = "Disable AutoRCM"; + ments[4].handler = _disable_autorcm; + } + ments[4].type = MENT_HDLR_RE; + + memset(&ments[5], 0, sizeof(ment_t)); + menu_t menu = {ments, "This corrupts your BOOT0!", 0, 0}; + + tui_do_menu(&gfx_con, &menu); +} + +int _fix_attributes(char *path, u32 *total, u32 is_root, u32 check_first_run) +{ + FRESULT res; + DIR dir; + u32 dirLength = 0; + static FILINFO fno; + + if (check_first_run) + { + // Read file attributes. + res = f_stat(path, &fno); + if (res != FR_OK) + return res; + + // Check if archive bit is set. + if (fno.fattrib & AM_ARC) + { + *(u32 *)total = *(u32 *)total + 1; + f_chmod(path, 0, AM_ARC); + } + } + + // Open directory. + res = f_opendir(&dir, path); + if (res != FR_OK) + return res; + + dirLength = strlen(path); + for (;;) + { + // Clear file or folder path. + path[dirLength] = 0; + + // Read a directory item. + res = f_readdir(&dir, &fno); + + // Break on error or end of dir. + if (res != FR_OK || fno.fname[0] == 0) + break; + + // Skip official Nintendo dir. + if (is_root && !strcmp(fno.fname, "Nintendo")) + continue; + + // Set new directory or file. + memcpy(&path[dirLength], "/", 1); + memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1); + + // Check if archive bit is set. + if (fno.fattrib & AM_ARC) + { + *(u32 *)total = *(u32 *)total + 1; + f_chmod(path, 0, AM_ARC); + } + + // Is it a directory? + if (fno.fattrib & AM_DIR) + { + // Enter the directory. + res = _fix_attributes(path, total, 0, 0); + if (res != FR_OK) + break; + } + } + + f_closedir(&dir); + + return res; +} + +void _fix_sd_attr(u32 type) +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + char path[256]; + char label[14]; + + u32 total = 0; + if (sd_mount()) + { + switch (type) + { + case 0: + memcpy(path, "/", 2); + memcpy(label, "SD Card", 8); + break; + case 1: + default: + memcpy(path, "/switch", 8); + memcpy(label, "switch folder", 14); + break; + } + + gfx_printf(&gfx_con, "Traversing all %s files!\nThis may take some time, please wait...\n\n", label); + _fix_attributes(path, &total, !type, type); + gfx_printf(&gfx_con, "%kTotal archive bits cleared: %d!%k\n\nDone! Press any key...", 0xFF96FF00, total, 0xFFCCCCCC); + sd_unmount(); + } + btn_wait(); +} + +void fix_sd_all_attr() { _fix_sd_attr(0); } +void fix_sd_switch_attr() { _fix_sd_attr(1); } + +void fix_battery_desync() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + max77620_low_battery_monitor_config(); + + gfx_puts(&gfx_con, "\nDone!\n"); + + btn_wait(); +} + +/* void fix_fuel_gauge_configuration() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + int battVoltage, avgCurrent; + + max17050_get_property(MAX17050_VCELL, &battVoltage); + max17050_get_property(MAX17050_AvgCurrent, &avgCurrent); + + // Check if still charging. If not, check if battery is >= 95% (4.1V). + if (avgCurrent < 0 && battVoltage > 4100) + { + if ((avgCurrent / 1000) < -10) + EPRINTF("You need to be connected to a wall adapter,\nto apply this fix!"); + else + { + gfx_printf(&gfx_con, "%kAre you really sure?\nThis will reset your fuel gauge completely!\n", 0xFFFFDD00); + gfx_printf(&gfx_con, "Additionally this will power off your console.\n%k", 0xFFCCCCCC); + + gfx_puts(&gfx_con, "\nPress POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); + + u32 btn = btn_wait(); + if (btn & BTN_POWER) + { + max17050_fix_configuration(); + msleep(1000); + gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); + u16 value = 0; + gfx_printf(&gfx_con, "%kThe console will power off in 45 seconds.\n%k", 0xFFFFDD00, 0xFFCCCCCC); + while (value < 46) + { + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + gfx_printf(&gfx_con, "%2ds elapsed", value); + msleep(1000); + value++; + } + msleep(2000); + + power_off(); + } + return; + } + } + else + EPRINTF("You need a fully charged battery\nand connected to a wall adapter,\nto apply this fix!"); + + msleep(500); + btn_wait(); +} */ + +/*void reset_pmic_fuel_gauge_charger_config() +{ + int avgCurrent; + + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + gfx_printf(&gfx_con, "%k\nThis will wipe your battery stats completely!\n" + "%kAnd it may not power on without physically\nremoving and re-inserting the battery.\n%k" + "\nAre you really sure?%k\n", 0xFFFFDD00, 0xFFFF0000, 0xFFFFDD00, 0xFFCCCCCC); + + gfx_puts(&gfx_con, "\nPress POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); + u32 btn = btn_wait(); + if (btn & BTN_POWER) + { + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + gfx_printf(&gfx_con, "%kKeep the USB cable connected!%k\n\n", 0xFFFFDD00, 0xFFCCCCCC); + gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); + + u8 value = 30; + while (value > 0) + { + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + gfx_printf(&gfx_con, "%kWait... (%ds) %k", 0xFF888888, value, 0xFFCCCCCC); + msleep(1000); + value--; + } + gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); + + //Check if still connected. + max17050_get_property(MAX17050_AvgCurrent, &avgCurrent); + if ((avgCurrent / 1000) < -10) + EPRINTF("You need to be connected to a wall adapter\nor PC to apply this fix!"); + else + { + // Apply fix. + bq24193_fake_battery_removal(); + gfx_printf(&gfx_con, "Done! \n" + "%k1. Remove the USB cable\n" + "2. Press POWER for 15s.\n" + "3. Reconnect the USB to power-on!%k\n", 0xFFFFDD00, 0xFFCCCCCC); + } + msleep(500); + btn_wait(); + } +}*/ + +/* +//#include "../modules/hekate_libsys_minerva/mtc.h" +//mtc_config_t mtc_cfg; + +void minerva() +{ + gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); + gfx_con_setpos(&gfx_con, 0, 0); + + u32 curr_ram_idx = 0; + + if (!sd_mount()) + return; + + gfx_printf(&gfx_con, "-- Minerva Training Cell --\n\n"); + + // Set table to ram. + mtc_cfg.mtc_table = NULL; + mtc_cfg.sdram_id = (fuse_read_odm(4) >> 3) & 0x1F; + ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); + + gfx_printf(&gfx_con, "\nStarting training process..\n\n"); + + // Get current frequency + for (curr_ram_idx = 0; curr_ram_idx < 10; curr_ram_idx++) + { + if (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) == mtc_cfg.mtc_table[curr_ram_idx].clk_src_emc) + break; + } + + mtc_cfg.rate_from = mtc_cfg.mtc_table[curr_ram_idx].rate_khz; + mtc_cfg.rate_to = 800000; + mtc_cfg.train_mode = OP_TRAIN_SWITCH; + gfx_printf(&gfx_con, "Training and switching %7d -> %7d\n\n", mtc_cfg.mtc_table[curr_ram_idx].rate_khz, 800000); + ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); + + // Thefollowing frequency needs periodic training every 100ms. + //msleep(200); + + //mtc_cfg.rate_to = 1600000; + //gfx_printf(&gfx_con, "Training and switching %7d -> %7d\n\n", mtc_cfg.current_emc_table->rate_khz, 1600000); + //ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); + + //mtc_cfg.train_mode = OP_PERIODIC_TRAIN; + + sd_unmount(); + + gfx_printf(&gfx_con, "Finished!"); + + btn_wait(); +} +*/ diff --git a/bootloader/frontend/fe_tools.h b/bootloader/frontend/fe_tools.h new file mode 100644 index 0000000..c459949 --- /dev/null +++ b/bootloader/frontend/fe_tools.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * + * 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 . + */ + +#ifndef _FE_TOOLS_H_ +#define _FE_TOOLS_H_ + +void dump_packages12(); +void fix_sd_all_attr(); +void fix_sd_switch_attr(); +void fix_battery_desync(); +void menu_autorcm(); + +#endif diff --git a/bootloader/main.c b/bootloader/main.c index 95f6e1d..09bbde9 100644 --- a/bootloader/main.c +++ b/bootloader/main.c @@ -1,10 +1,7 @@ /* * Copyright (c) 2018 naehrwert * - * Copyright (c) 2018 Rajko Stojadinovic * Copyright (c) 2018 CTCaer - * Copyright (c) 2018 Reisyukaku - * 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, @@ -22,46 +19,32 @@ #include #include -#include "soc/clock.h" -#include "soc/uart.h" -#include "soc/i2c.h" -#include "mem/sdram.h" +#include "config/config.h" #include "gfx/di.h" -#include "mem/mc.h" -#include "soc/t210.h" -#include "soc/pmc.h" -#include "soc/pinmux.h" -#include "soc/fuse.h" -#include "utils/util.h" #include "gfx/gfx.h" -#include "utils/btn.h" -#include "sec/tsec.h" -#include "soc/kfuse.h" -#include "power/max77620.h" -#include "power/max7762x.h" -#include "soc/gpio.h" -#include "storage/sdmmc.h" -#include "libs/fatfs/ff.h" #include "gfx/logos.h" #include "gfx/tui.h" -#include "mem/heap.h" -#include "utils/list.h" -#include "storage/nx_emmc.h" -#include "sec/se.h" -#include "sec/se_t210.h" #include "hos/hos.h" -#include "hos/pkg1.h" -#include "hos/pkg2.h" -#include "storage/mmc.h" -#include "libs/compr/blz.h" -#include "power/max17050.h" -#include "power/bq24193.h" -#include "config/config.h" #include "ianos/ianos.h" +#include "libs/compr/blz.h" +#include "libs/fatfs/ff.h" +#include "mem/heap.h" +#include "mem/sdram.h" +#include "power/max77620.h" +#include "soc/hw_init.h" +#include "soc/i2c.h" +#include "soc/pmc.h" +#include "soc/t210.h" +#include "soc/uart.h" +#include "storage/sdmmc.h" +#include "utils/btn.h" #include "utils/dirlist.h" +#include "utils/list.h" +#include "utils/util.h" -#define BOOTLOADER_UPDATED_MAGIC_ADDR 0x4003E000 -#define BOOTLOADER_UPDATED_MAGIC 0x424f4f54 +#include "frontend/fe_emmc_tools.h" +#include "frontend/fe_tools.h" +#include "frontend/fe_info.h" //TODO: ugly. gfx_ctxt_t gfx_ctxt; @@ -276,1569 +259,14 @@ void check_power_off_from_hos() } } -void config_oscillators() -{ - CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) = (CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) & 0xFFFFFFF3) | 4; - SYSCTR0(SYSCTR0_CNTFID0) = 19200000; - TMR(TIMERUS_USEC_CFG) = 0x45F; // For 19.2MHz clk_m. - CLOCK(CLK_RST_CONTROLLER_OSC_CTRL) = 0x50000071; - PMC(APBDEV_PMC_OSC_EDPD_OVER) = (PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFFFFF81) | 0xE; - PMC(APBDEV_PMC_OSC_EDPD_OVER) = (PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFBFFFFF) | 0x400000; - PMC(APBDEV_PMC_CNTRL2) = (PMC(APBDEV_PMC_CNTRL2) & 0xFFFFEFFF) | 0x1000; - PMC(APBDEV_PMC_SCRATCH188) = (PMC(APBDEV_PMC_SCRATCH188) & 0xFCFFFFFF) | 0x2000000; - CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 0x10; - CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) &= 0xBFFFFFFF; - PMC(APBDEV_PMC_TSC_MULT) = (PMC(APBDEV_PMC_TSC_MULT) & 0xFFFF0000) | 0x249F; //0x249F = 19200000 * (16 / 32.768 kHz) - CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = 0x20004444; - CLOCK(CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER) = 0x80000000; - CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 2; -} - -void config_gpios() -{ - PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0; - PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0; - - PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE; - PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE; - - gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); - gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO); - gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO); - gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO); - gpio_output_enable(GPIO_PORT_G, GPIO_PIN_0, GPIO_OUTPUT_DISABLE); - gpio_output_enable(GPIO_PORT_D, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); - gpio_output_enable(GPIO_PORT_E, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); - gpio_output_enable(GPIO_PORT_H, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); - - pinmux_config_i2c(I2C_1); - pinmux_config_i2c(I2C_5); - pinmux_config_uart(UART_A); - - // Configure volume up/down as inputs. - gpio_config(GPIO_PORT_X, GPIO_PIN_6, GPIO_MODE_GPIO); - gpio_config(GPIO_PORT_X, GPIO_PIN_7, GPIO_MODE_GPIO); - gpio_output_enable(GPIO_PORT_X, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); - gpio_output_enable(GPIO_PORT_X, GPIO_PIN_7, GPIO_OUTPUT_DISABLE); -} - -void config_pmc_scratch() -{ - PMC(APBDEV_PMC_SCRATCH20) &= 0xFFF3FFFF; - PMC(APBDEV_PMC_SCRATCH190) &= 0xFFFFFFFE; - PMC(APBDEV_PMC_SECURE_SCRATCH21) |= 0x10; -} - -void mbist_workaround() -{ - CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) | 0x8000) & 0xFFFFBFFF; - CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) |= 0x40800000u; - CLOCK(CLK_RST_CONTROLLER_RST_DEV_Y_CLR) = 0x40; - CLOCK(CLK_RST_CONTROLLER_RST_DEV_X_CLR) = 0x40000; - CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = 0x18000000; - usleep(2); - - I2S(I2S1_CTRL) |= I2S_CTRL_MASTER_EN; - I2S(I2S1_CG) &= ~I2S_CG_SLCG_ENABLE; - I2S(I2S2_CTRL) |= I2S_CTRL_MASTER_EN; - I2S(I2S2_CG) &= ~I2S_CG_SLCG_ENABLE; - I2S(I2S3_CTRL) |= I2S_CTRL_MASTER_EN; - I2S(I2S3_CG) &= ~I2S_CG_SLCG_ENABLE; - I2S(I2S4_CTRL) |= I2S_CTRL_MASTER_EN; - I2S(I2S4_CG) &= ~I2S_CG_SLCG_ENABLE; - I2S(I2S5_CTRL) |= I2S_CTRL_MASTER_EN; - I2S(I2S5_CG) &= ~I2S_CG_SLCG_ENABLE; - DISPLAY_A(_DIREG(DC_COM_DSC_TOP_CTL)) |= 4; - VIC(0x8C) = 0xFFFFFFFF; - usleep(2); - - CLOCK(CLK_RST_CONTROLLER_RST_DEV_Y_SET) = 0x40; - CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = 0x18000000; - CLOCK(CLK_RST_CONTROLLER_RST_DEV_X_SET) = 0x40000; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_H) = 0xC0; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) = 0x80000130; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) = 0x1F00200; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) = 0x80400808; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_W) = 0x402000FC; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X) = 0x23000780; - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) = 0x300; - CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA) = 0; - CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB) = 0; - CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC) = 0; - CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD) = 0; - CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE) = 0; - CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) &= 0x1F7FFFFF; - CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) &= 0xFFFF3FFF; - CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_VI) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_VI) & 0x1FFFFFFF) | 0x80000000; - CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X) & 0x1FFFFFFF) | 0x80000000; - CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_NVENC) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_NVENC) & 0x1FFFFFFF) | 0x80000000; -} - -void config_se_brom() -{ - // Bootrom part we skipped. - u32 sbk[4] = { - FUSE(FUSE_PRIVATE_KEY0), - FUSE(FUSE_PRIVATE_KEY1), - FUSE(FUSE_PRIVATE_KEY2), - FUSE(FUSE_PRIVATE_KEY3) - }; - // Set SBK to slot 14. - se_aes_key_set(14, sbk, 0x10); - - // Lock SBK from being read. - SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 14 * 4) = 0x7E; - - // This memset needs to happen here, else TZRAM will behave weirdly later on. - memset((void *)TZRAM_BASE, 0, 0x10000); - PMC(APBDEV_PMC_CRYPTO_OP) = 0; - SE(SE_INT_STATUS_REG_OFFSET) = 0x1F; - - // Lock SSK (although it's not set and unused anyways). - SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 15 * 4) = 0x7E; - - // Clear the boot reason to avoid problems later - PMC(APBDEV_PMC_SCRATCH200) = 0x0; - PMC(APBDEV_PMC_RST_STATUS) = 0x0; - APB_MISC(APB_MISC_PP_STRAPPING_OPT_A) |= (7 << 10); -} - -void config_hw() -{ - // Bootrom stuff we skipped by going through rcm. - config_se_brom(); - //FUSE(FUSE_PRIVATEKEYDISABLE) = 0x11; - SYSREG(AHB_AHB_SPARE_REG) &= 0xFFFFFF9F; - PMC(APBDEV_PMC_SCRATCH49) = ((PMC(APBDEV_PMC_SCRATCH49) >> 1) << 1) & 0xFFFFFFFD; - - mbist_workaround(); - clock_enable_se(); - - // Enable fuse clock. - clock_enable_fuse(true); - // Disable fuse programming. - fuse_disable_program(); - - mc_enable(); - - config_oscillators(); - APB_MISC(APB_MISC_PP_PINMUX_GLOBAL) = 0; - config_gpios(); - - //clock_enable_uart(UART_C); - //uart_init(UART_C, 115200); - - clock_enable_cl_dvfs(); - - clock_enable_i2c(I2C_1); - clock_enable_i2c(I2C_5); - - clock_enable_unk2(); - - i2c_init(I2C_1); - i2c_init(I2C_5); - - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CNFGBBC, 0x40); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, 0x78); - - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG0, 0x38); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG1, 0x3A); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG2, 0x38); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_LDO4, 0xF); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_LDO8, 0xC7); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD0, 0x4F); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD1, 0x29); - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD3, 0x1B); - - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_GPIO3, 0x22); // 3.x+ - - i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD0, 42); //42 = (1125000uV - 600000) / 12500 -> 1.125V - - config_pmc_scratch(); // Missing from 4.x+ - - CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = (CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) & 0xFFFF8888) | 0x3333; - - sdram_init(); -} - -void reconfig_hw_workaround(bool extra_reconfig, u32 magic) -{ - // Re-enable clocks to Audio Processing Engine as a workaround to hanging. - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) |= (1 << 10); // Enable AHUB clock. - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) |= (1 << 6); // Enable APE clock. - - if (extra_reconfig) - { - msleep(10); - PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN; - - clock_disable_cl_dvfs(); - - // Disable Joy-con GPIOs. - gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); - gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); - gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_SPIO); - gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_SPIO); - } - - // Power off display. - display_end(); - - // Enable clock to USBD and init SDMMC1 to avoid hangs with bad hw inits. - if (magic == 0xBAADF00D) - { - CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) |= (1 << 22); - sdmmc_init(&sd_sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0); - clock_disable_cl_dvfs(); - - msleep(200); - } -} - -void print_fuseinfo() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - u32 burntFuses = 0; - for (u32 i = 0; i < 32; i++) - { - if ((fuse_read_odm(7) >> i) & 1) - burntFuses++; - } - - gfx_printf(&gfx_con, "\nSKU: %X - ", FUSE(FUSE_SKU_INFO)); - switch (fuse_read_odm(4) & 3) - { - case 0: - gfx_printf(&gfx_con, "Retail\n"); - break; - case 3: - gfx_printf(&gfx_con, "Dev\n"); - break; - } - gfx_printf(&gfx_con, "Sdram ID: %d\n", (fuse_read_odm(4) >> 3) & 0x1F); - gfx_printf(&gfx_con, "Burnt fuses: %d\n", burntFuses); - gfx_printf(&gfx_con, "Secure key: %08X%08X%08X%08X\n\n\n", - 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))); - - gfx_printf(&gfx_con, "%k(Unlocked) fuse cache:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC); - gfx_hexdump(&gfx_con, 0x7000F900, (u8 *)0x7000F900, 0x2FC); - - gfx_puts(&gfx_con, "Press POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); - - u32 btn = btn_wait(); - if (btn & BTN_POWER) - { - if (sd_mount()) - { - char path[64]; - emmcsn_path_impl(path, "/dumps", "fuses.bin", NULL); - if (!sd_save_to_file((u8 *)0x7000F900, 0x2FC, path)) - gfx_puts(&gfx_con, "\nDone!\n"); - sd_unmount(); - } - - btn_wait(); - } -} - -void print_kfuseinfo() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - gfx_printf(&gfx_con, "%kKFuse contents:\n\n%k", 0xFF00DDFF, 0xFFCCCCCC); - u32 buf[KFUSE_NUM_WORDS]; - if (!kfuse_read(buf)) - EPRINTF("CRC fail."); - else - gfx_hexdump(&gfx_con, 0, (u8 *)buf, KFUSE_NUM_WORDS * 4); - - gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); - - 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)) - gfx_puts(&gfx_con, "\nDone!\n"); - sd_unmount(); - } - - btn_wait(); - } -} - -void print_mmc_info() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - static const u32 SECTORS_TO_MIB_COEFF = 11; - - sdmmc_storage_t storage; - sdmmc_t sdmmc; - - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - goto out; - } - else - { - u16 card_type; - u32 speed = 0; - - gfx_printf(&gfx_con, "%kCID:%k\n", 0xFF00DDFF, 0xFFCCCCCC); - switch (storage.csd.mmca_vsn) - { - case 0: /* MMC v1.0 - v1.2 */ - case 1: /* MMC v1.4 */ - gfx_printf(&gfx_con, - " Vendor ID: %03X\n" - " Model: %c%c%c%c%c%c%c\n" - " HW rev: %X\n" - " FW rev: %X\n" - " S/N: %03X\n" - " Month/Year: %02d/%04d\n\n", - storage.cid.manfid, - storage.cid.prod_name[0], storage.cid.prod_name[1], storage.cid.prod_name[2], - storage.cid.prod_name[3], storage.cid.prod_name[4], storage.cid.prod_name[5], - storage.cid.prod_name[6], storage.cid.hwrev, storage.cid.fwrev, - storage.cid.serial, storage.cid.month, storage.cid.year); - break; - case 2: /* MMC v2.0 - v2.2 */ - case 3: /* MMC v3.1 - v3.3 */ - case 4: /* MMC v4 */ - gfx_printf(&gfx_con, - " Vendor ID: %X\n" - " Card/BGA: %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", - storage.cid.manfid, storage.cid.card_bga, storage.cid.oemid, - storage.cid.prod_name[0], storage.cid.prod_name[1], storage.cid.prod_name[2], - storage.cid.prod_name[3], storage.cid.prod_name[4], storage.cid.prod_name[5], - storage.cid.prv, storage.cid.serial, storage.cid.month, storage.cid.year); - break; - default: - EPRINTFARGS("eMMC has unknown MMCA version %d", storage.csd.mmca_vsn); - break; - } - - if (storage.csd.structure == 0) - EPRINTF("Unknown CSD structure."); - else - { - gfx_printf(&gfx_con, "%kExtended CSD V1.%d:%k\n", - 0xFF00DDFF, storage.ext_csd.ext_struct, 0xFFCCCCCC); - card_type = storage.ext_csd.card_type; - u8 card_type_support[96]; - u8 pos_type = 0; - card_type_support[0] = 0; - if (card_type & EXT_CSD_CARD_TYPE_HS_26) - { - memcpy(card_type_support, "HS26", 4); - speed = (26 << 16) | 26; - pos_type += 4; - } - if (card_type & EXT_CSD_CARD_TYPE_HS_52) - { - memcpy(card_type_support + pos_type, ", HS52", 6); - speed = (52 << 16) | 52; - pos_type += 6; - } - if (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) - { - memcpy(card_type_support + pos_type, ", DDR52_1.8V", 12); - speed = (52 << 16) | 104; - pos_type += 12; - } - if (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) - { - memcpy(card_type_support + pos_type, ", HS200_1.8V", 12); - speed = (200 << 16) | 200; - pos_type += 12; - } - if (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) - { - memcpy(card_type_support + pos_type, ", HS400_1.8V", 12); - speed = (200 << 16) | 400; - pos_type += 12; - } - card_type_support[pos_type] = 0; - - gfx_printf(&gfx_con, - " 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: ", - storage.csd.mmca_vsn, storage.ext_csd.rev, storage.ext_csd.dev_version, storage.csd.cmdclass, - storage.csd.capacity == (4096 * 512) ? "High" : "Low", speed & 0xFFFF, (speed >> 16) & 0xFFFF, - storage.csd.busspeed); - gfx_con.fntsz = 8; - gfx_printf(&gfx_con, "%s", card_type_support); - gfx_con.fntsz = 16; - gfx_printf(&gfx_con, "\n\n", card_type_support); - - u32 boot_size = storage.ext_csd.boot_mult << 17; - u32 rpmb_size = storage.ext_csd.rpmb_mult << 17; - gfx_printf(&gfx_con, "%keMMC Partitions:%k\n", 0xFF00DDFF, 0xFFCCCCCC); - gfx_printf(&gfx_con, " 1: %kBOOT0 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, - boot_size / 1024, boot_size / 1024 / 512); - gfx_put_small_sep(&gfx_con); - gfx_printf(&gfx_con, " 2: %kBOOT1 %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, - boot_size / 1024, boot_size / 1024 / 512); - gfx_put_small_sep(&gfx_con); - gfx_printf(&gfx_con, " 3: %kRPMB %k\n Size: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF96FF00, 0xFFCCCCCC, - rpmb_size / 1024, rpmb_size / 1024 / 512); - gfx_put_small_sep(&gfx_con); - gfx_printf(&gfx_con, " 0: %kGPP (USER) %k\n Size: %5d MiB (LBA Sectors: 0x%07X)\n\n", 0xFF96FF00, 0xFFCCCCCC, - storage.sec_cnt >> SECTORS_TO_MIB_COEFF, storage.sec_cnt); - gfx_put_small_sep(&gfx_con); - gfx_printf(&gfx_con, "%kGPP (eMMC USER) partition table:%k\n", 0xFF00DDFF, 0xFFCCCCCC); - - sdmmc_storage_set_mmc_partition(&storage, 0); - LIST_INIT(gpt); - nx_emmc_gpt_parse(&gpt, &storage); - int gpp_idx = 0; - LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) - { - gfx_printf(&gfx_con, " %02d: %k%s%k\n Size: % 5d MiB (LBA Sectors 0x%07X)\n LBA Range: %08X-%08X\n", - 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); - gfx_put_small_sep(&gfx_con); - } - nx_emmc_gpt_free(&gpt); - } - } - -out: - sdmmc_storage_end(&storage); - - btn_wait(); -} - -void print_sdcard_info() -{ - static const u32 SECTORS_TO_MIB_COEFF = 11; - - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - if (sd_mount()) - { - u32 capacity; - - gfx_printf(&gfx_con, "%kCard IDentification:%k\n", 0xFF00DDFF, 0xFFCCCCCC); - gfx_printf(&gfx_con, - " 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); - - gfx_printf(&gfx_con, "%kCard-Specific Data V%d.0:%k\n", 0xFF00DDFF, sd_storage.csd.structure + 1, 0xFFCCCCCC); - capacity = sd_storage.csd.capacity >> (20 - sd_storage.csd.read_blkbits); - gfx_printf(&gfx_con, - " 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" - " Write Protect: %d\n\n", - sd_storage.csd.cmdclass, capacity, - 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, - sd_storage.ssr.app_class, sd_storage.csd.write_protect); - - gfx_puts(&gfx_con, "Acquiring FAT volume info...\n\n"); - f_getfree("", &sd_fs.free_clst, NULL); - gfx_printf(&gfx_con, "%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); - sd_unmount(); - } - - btn_wait(); -} - -void print_tsec_key() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - sdmmc_storage_t storage; - sdmmc_t sdmmc; - - sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4); - - // Read package1. - u8 *pkg1 = (u8 *)malloc(0x40000); - sdmmc_storage_set_mmc_partition(&storage, 1); - sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); - sdmmc_storage_end(&storage); - const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); - if (!pkg1_id) - { - EPRINTFARGS("Unknown package1 version for reading\nTSEC firmware (= '%s').", - (char *)pkg1 + 0x10); - goto out_wait; - } - - u8 keys[0x10 * 3]; - for (u32 i = 1; i <= 3; i++) - { - int res = tsec_query(keys + ((i - 1) * 0x10), i, pkg1 + pkg1_id->tsec_off); - - gfx_printf(&gfx_con, "%kTSEC key %d: %k", 0xFF00DDFF, i, 0xFFCCCCCC); - if (res >= 0) - { - for (u32 j = 0; j < 0x10; j++) - gfx_printf(&gfx_con, "%02X", keys[((i - 1) * 0x10) + j]); - } - else - EPRINTFARGS("ERROR %X", res); - gfx_putc(&gfx_con, '\n'); - } - - gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); - - u32 btn = btn_wait(); - if (btn & BTN_POWER) - { - if (sd_mount()) - { - char path[64]; - emmcsn_path_impl(path, "/dumps", "tsec_keys.bin", NULL); - if (!sd_save_to_file(keys, 0x10 * 3, path)) - gfx_puts(&gfx_con, "\nDone!\n"); - sd_unmount(); - } - } - else - goto out; - -out_wait: - btn_wait(); - -out: - free(pkg1); -} - -int dump_emmc_verify(sdmmc_storage_t *storage, u32 lba_curr, char *outFilename, emmc_part_t *part) -{ - FIL fp; - u32 btn = 0; - u32 prevPct = 200; - int res = 0; - - u8 hashEm[0x20]; - u8 hashSd[0x20]; - - if (f_open(&fp, outFilename, FA_READ) == FR_OK) - { - u32 totalSectorsVer = (u32)((u64)f_size(&fp) >> (u64)9); - - u32 numSectorsPerIter = 0; - if (totalSectorsVer > 0x200000) - numSectorsPerIter = 8192; //4MB Cache - else - numSectorsPerIter = 512; //256KB Cache - - u8 *bufEm = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); - u8 *bufSd = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); - - u32 pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); - tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFF96FF00, 0xFF155500); - - u32 num = 0; - while (totalSectorsVer > 0) - { - num = MIN(totalSectorsVer, numSectorsPerIter); - - if (!sdmmc_storage_read(storage, lba_curr, num, bufEm)) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFailed to read %d blocks (@LBA %08X),\nfrom eMMC!\n\nVerification failed..\n", - num, lba_curr); - - free(bufEm); - free(bufSd); - f_close(&fp); - return 1; - } - if (f_read(&fp, bufSd, num << 9, NULL)) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFailed to read %d blocks (@LBA %08X),\nfrom sd card!\n\nVerification failed..\n", num, lba_curr); - - free(bufEm); - free(bufSd); - f_close(&fp); - return 1; - } - - switch (h_cfg.verification) - { - case 1: - res = memcmp32sparse((u32 *)bufEm, (u32 *)bufSd, num << 9); - break; - case 2: - default: - se_calc_sha256(&hashEm, bufEm, num << 9); - se_calc_sha256(&hashSd, bufSd, num << 9); - res = memcmp(hashEm, hashSd, 0x10); - break; - } - if (res) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nSD card and eMMC data (@LBA %08X),\ndo not match!\n\nVerification failed..\n", lba_curr); - - free(bufEm); - free(bufSd); - f_close(&fp); - return 1; - } - - pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); - if (pct != prevPct) - { - tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFF96FF00, 0xFF155500); - prevPct = pct; - } - - lba_curr += num; - totalSectorsVer -= num; - - btn = btn_wait_timeout(0, BTN_VOL_DOWN | BTN_VOL_UP); - if ((btn & BTN_VOL_DOWN) && (btn & BTN_VOL_UP)) - { - gfx_con.fntsz = 16; - WPRINTF("\n\nThe verification was cancelled!"); - EPRINTF("\nPress any key...\n"); - msleep(1500); - - free(bufEm); - free(bufSd); - f_close(&fp); - - return 0; - } - } - free(bufEm); - free(bufSd); - f_close(&fp); - - tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); - - return 0; - } - else - { - gfx_con.fntsz = 16; - EPRINTF("\nFile not found or could not be loaded.\n\nVerification failed..\n"); - return 1; - } -} - -int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part) -{ - static const u32 FAT32_FILESIZE_LIMIT = 0xFFFFFFFF; - static const u32 SECTORS_TO_MIB_COEFF = 11; - - u32 multipartSplitSize = (1u << 31); - u32 totalSectors = part->lba_end - part->lba_start + 1; - u32 currPartIdx = 0; - u32 numSplitParts = 0; - u32 maxSplitParts = 0; - u32 btn = 0; - bool isSmallSdCard = false; - bool partialDumpInProgress = false; - int res = 0; - char *outFilename = sd_path; - u32 sdPathLen = strlen(sd_path); - - FIL partialIdxFp; - char partialIdxFilename[12]; - memcpy(partialIdxFilename, "partial.idx", 12); - - gfx_con.fntsz = 8; - gfx_printf(&gfx_con, "\nSD Card free space: %d MiB, Total backup size %d MiB\n\n", - sd_fs.free_clst * sd_fs.csize >> SECTORS_TO_MIB_COEFF, - totalSectors >> SECTORS_TO_MIB_COEFF); - - // 1GB parts for sd cards 8GB and less. - if ((sd_storage.csd.capacity >> (20 - sd_storage.csd.read_blkbits)) <= 8192) - multipartSplitSize = (1u << 30); - // Maximum parts fitting the free space available. - maxSplitParts = (sd_fs.free_clst * sd_fs.csize) / (multipartSplitSize / 512); - - // Check if the USER partition or the RAW eMMC fits the sd card free space. - if (totalSectors > (sd_fs.free_clst * sd_fs.csize)) - { - isSmallSdCard = true; - - gfx_printf(&gfx_con, "%k\nSD card free space is smaller than total backup size.%k\n", 0xFFFFBA00, 0xFFCCCCCC); - - if (!maxSplitParts) - { - gfx_con.fntsz = 16; - EPRINTF("Not enough free space for Partial Backup."); - - return 0; - } - } - // Check if we are continuing a previous raw eMMC or USER partition backup in progress. - if (f_open(&partialIdxFp, partialIdxFilename, FA_READ) == FR_OK && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) - { - gfx_printf(&gfx_con, "%kFound Partial Backup in progress. Continuing...%k\n\n", 0xFFAEFD14, 0xFFCCCCCC); - - partialDumpInProgress = true; - // Force partial dumping, even if the card is larger. - isSmallSdCard = true; - - f_read(&partialIdxFp, &currPartIdx, 4, NULL); - f_close(&partialIdxFp); - - if (!maxSplitParts) - { - gfx_con.fntsz = 16; - EPRINTF("Not enough free space for Partial Backup."); - - return 0; - } - - // Increase maxSplitParts to accommodate previously backed up parts. - maxSplitParts += currPartIdx; - } - else if (isSmallSdCard) - gfx_printf(&gfx_con, "%kPartial Backup enabled (with %d MiB parts)...%k\n\n", 0xFFFFBA00, multipartSplitSize >> 20, 0xFFCCCCCC); - - // Check if filesystem is FAT32 or the free space is smaller and backup in parts. - if (((sd_fs.fs_type != FS_EXFAT) && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) | isSmallSdCard) - { - u32 multipartSplitSectors = multipartSplitSize / NX_EMMC_BLOCKSIZE; - numSplitParts = (totalSectors + multipartSplitSectors - 1) / multipartSplitSectors; - - outFilename[sdPathLen++] = '.'; - - if (!partialDumpInProgress) - { - outFilename[sdPathLen] = '0'; - if (numSplitParts >= 10) - { - outFilename[sdPathLen + 1] = '0'; - outFilename[sdPathLen + 2] = 0; - } - else - outFilename[sdPathLen + 1] = 0; - } - // Continue from where we left, if Partial Backup in progress. - else - { - if (numSplitParts >= 10 && currPartIdx < 10) - { - outFilename[sdPathLen] = '0'; - itoa(currPartIdx, &outFilename[sdPathLen + 1], 10); - } - else - itoa(currPartIdx, &outFilename[sdPathLen], 10); - } - } - - FIL fp; - gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); - if (!f_open(&fp, outFilename, FA_READ)) - { - f_close(&fp); - gfx_con.fntsz = 16; - - WPRINTF("An existing backup has been detected!"); - WPRINTF("Press POWER to Continue.\nPress VOL to go to the menu.\n"); - msleep(500); - - if (!(btn_wait() & BTN_POWER)) - return 0; - gfx_con.fntsz = 8; - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, gfx_con.savedy, 48); - } - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - gfx_printf(&gfx_con, "Filename: %s\n\n", outFilename); - res = f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE); - if (res) - { - gfx_con.fntsz = 16; - EPRINTFARGS("Error (%d) creating file %s.\n", res, outFilename); - - return 0; - } - - u32 numSectorsPerIter = 0; - if (totalSectors > 0x200000) - numSectorsPerIter = 8192; - else - numSectorsPerIter = 512; - u8 *buf = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); - - u32 lba_curr = part->lba_start; - u32 lbaStartPart = part->lba_start; - u32 bytesWritten = 0; - u32 prevPct = 200; - int retryCount = 0; - - // Continue from where we left, if Partial Backup in progress. - if (partialDumpInProgress) - { - lba_curr += currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE); - totalSectors -= currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE); - lbaStartPart = lba_curr; // Update the start LBA for verification. - } - u64 totalSize = (u64)((u64)totalSectors << 9); - if (!isSmallSdCard && sd_fs.fs_type == FS_EXFAT) - f_lseek(&fp, totalSize); - else - f_lseek(&fp, MIN(totalSize, multipartSplitSize)); - f_lseek(&fp, 0); - - u32 num = 0; - u32 pct = 0; - while (totalSectors > 0) - { - if (numSplitParts != 0 && bytesWritten >= multipartSplitSize) - { - f_close(&fp); - memset(&fp, 0, sizeof(fp)); - currPartIdx++; - - if (h_cfg.verification) - { - // Verify part. - if (dump_emmc_verify(storage, lbaStartPart, outFilename, part)) - { - EPRINTF("\nPress any key and try again...\n"); - - free(buf); - return 0; - } - } - - if (numSplitParts >= 10 && currPartIdx < 10) - { - outFilename[sdPathLen] = '0'; - itoa(currPartIdx, &outFilename[sdPathLen + 1], 10); - } - else - itoa(currPartIdx, &outFilename[sdPathLen], 10); - - // Always create partial.idx before next part, in case a fatal error occurs. - if (isSmallSdCard) - { - // Create partial backup index file. - if (f_open(&partialIdxFp, partialIdxFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) - { - f_write(&partialIdxFp, &currPartIdx, 4, NULL); - f_close(&partialIdxFp); - } - else - { - gfx_con.fntsz = 16; - EPRINTF("\nError creating partial.idx file.\n"); - - free(buf); - return 0; - } - - // More parts to backup that do not currently fit the sd card free space or fatal error. - if (currPartIdx >= maxSplitParts) - { - gfx_puts(&gfx_con, "\n\n1. Press any key to unmount SD Card.\n\ - 2. Remove SD Card and move files to free space.\n\ - Don\'t move the partial.idx file!\n\ - 3. Re-insert SD Card.\n\ - 4. Select the SAME option again to continue.\n"); - gfx_con.fntsz = 16; - - free(buf); - return 1; - } - } - - // Create next part. - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - gfx_printf(&gfx_con, "Filename: %s\n\n", outFilename); - lbaStartPart = lba_curr; - res = f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE); - if (res) - { - gfx_con.fntsz = 16; - EPRINTFARGS("Error (%d) creating file %s.\n", res, outFilename); - - free(buf); - return 0; - } - bytesWritten = 0; - - totalSize = (u64)((u64)totalSectors << 9); - f_lseek(&fp, MIN(totalSize, multipartSplitSize)); - f_lseek(&fp, 0); - } - - retryCount = 0; - num = MIN(totalSectors, numSectorsPerIter); - while (!sdmmc_storage_read(storage, lba_curr, num, buf)) - { - EPRINTFARGS("Error reading %d blocks @ LBA %08X,\nfrom eMMC (try %d), retrying...", - num, lba_curr, ++retryCount); - - msleep(150); - if (retryCount >= 3) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFailed to read %d blocks @ LBA %08X\nfrom eMMC. Aborting..\n", - num, lba_curr); - EPRINTF("\nPress any key and try again...\n"); - - free(buf); - f_close(&fp); - f_unlink(outFilename); - - return 0; - } - } - res = f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL); - if (res) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFatal error (%d) when writing to SD Card", res); - EPRINTF("\nPress any key and try again...\n"); - - free(buf); - f_close(&fp); - f_unlink(outFilename); - - return 0; - } - pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); - if (pct != prevPct) - { - tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); - prevPct = pct; - } - - lba_curr += num; - totalSectors -= num; - bytesWritten += num * NX_EMMC_BLOCKSIZE; - - // Force a flush after a lot of data if not splitting. - if (numSplitParts == 0 && bytesWritten >= multipartSplitSize) - { - f_sync(&fp); - bytesWritten = 0; - } - - btn = btn_wait_timeout(0, BTN_VOL_DOWN | BTN_VOL_UP); - if ((btn & BTN_VOL_DOWN) && (btn & BTN_VOL_UP)) - { - gfx_con.fntsz = 16; - WPRINTF("\n\nThe backup was cancelled!"); - EPRINTF("\nPress any key...\n"); - msleep(1500); - - free(buf); - f_close(&fp); - f_unlink(outFilename); - - return 0; - } - } - tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFFCCCCCC, 0xFF555555); - - // Backup operation ended successfully. - free(buf); - f_close(&fp); - - if (h_cfg.verification) - { - // Verify last part or single file backup. - if (dump_emmc_verify(storage, lbaStartPart, outFilename, part)) - { - EPRINTF("\nPress any key and try again...\n"); - - return 0; - } - else - tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFF96FF00, 0xFF155500); - } - - gfx_con.fntsz = 16; - // Remove partial backup index file if no fatal errors occurred. - if (isSmallSdCard) - { - f_unlink(partialIdxFilename); - gfx_printf(&gfx_con, "%k\n\nYou can now join the files\nand get the complete eMMC RAW GPP backup.", 0xFFCCCCCC); - } - gfx_puts(&gfx_con, "\n\n"); - - return 1; -} - -typedef enum -{ - PART_BOOT = (1 << 0), - PART_SYSTEM = (1 << 1), - PART_USER = (1 << 2), - PART_RAW = (1 << 3), - PART_GP_ALL = (1 << 7) -} emmcPartType_t; - -static void dump_emmc_selected(emmcPartType_t dumpType) -{ - int res = 0; - u32 timer = 0; - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - tui_sbar(&gfx_con, true); - gfx_con_setpos(&gfx_con, 0, 0); - - if (!sd_mount()) - goto out; - - gfx_puts(&gfx_con, "Checking for available free space...\n\n"); - // Get SD Card free space for Partial Backup. - f_getfree("", &sd_fs.free_clst, NULL); - - sdmmc_storage_t storage; - sdmmc_t sdmmc; - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - goto out; - } - - int i = 0; - char sdPath[80]; - // Create Restore folders, if they do not exist. - emmcsn_path_impl(sdPath, "/restore", "", &storage); - emmcsn_path_impl(sdPath, "/restore/partitions", "", &storage); - - timer = get_tmr_s(); - if (dumpType & PART_BOOT) - { - const u32 BOOT_PART_SIZE = storage.ext_csd.boot_mult << 17; - - emmc_part_t bootPart; - memset(&bootPart, 0, sizeof(bootPart)); - bootPart.lba_start = 0; - bootPart.lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1; - for (i = 0; i < 2; i++) - { - memcpy(bootPart.name, "BOOT", 5); - bootPart.name[4] = (u8)('0' + i); - bootPart.name[5] = 0; - - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i, - bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFCCCCCC); - - sdmmc_storage_set_mmc_partition(&storage, i + 1); - - emmcsn_path_impl(sdPath, "", bootPart.name, &storage); - res = dump_emmc_part(sdPath, &storage, &bootPart); - } - } - - if ((dumpType & PART_SYSTEM) || (dumpType & PART_USER) || (dumpType & PART_RAW)) - { - sdmmc_storage_set_mmc_partition(&storage, 0); - - if ((dumpType & PART_SYSTEM) || (dumpType & PART_USER)) - { - LIST_INIT(gpt); - nx_emmc_gpt_parse(&gpt, &storage); - LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) - { - if ((dumpType & PART_USER) == 0 && !strcmp(part->name, "USER")) - continue; - if ((dumpType & PART_SYSTEM) == 0 && strcmp(part->name, "USER")) - continue; - - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, - part->name, part->lba_start, part->lba_end, 0xFFCCCCCC); - - emmcsn_path_impl(sdPath, "/partitions", part->name, &storage); - res = dump_emmc_part(sdPath, &storage, part); - // If a part failed, don't continue. - if (!res) - break; - } - nx_emmc_gpt_free(&gpt); - } - - if (dumpType & PART_RAW) - { - // Get GP partition size dynamically. - const u32 RAW_AREA_NUM_SECTORS = storage.sec_cnt; - - emmc_part_t rawPart; - memset(&rawPart, 0, sizeof(rawPart)); - rawPart.lba_start = 0; - rawPart.lba_end = RAW_AREA_NUM_SECTORS - 1; - strcpy(rawPart.name, "rawnand.bin"); - { - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, - rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFCCCCCC); - - emmcsn_path_impl(sdPath, "", rawPart.name, &storage); - res = dump_emmc_part(sdPath, &storage, &rawPart); - } - } - } - - gfx_putc(&gfx_con, '\n'); - timer = get_tmr_s() - timer; - gfx_printf(&gfx_con, "Time taken: %dm %ds.\n", timer / 60, timer % 60); - sdmmc_storage_end(&storage); - if (res && h_cfg.verification) - gfx_printf(&gfx_con, "\n%kFinished and verified!%k\nPress any key...\n", 0xFF96FF00, 0xFFCCCCCC); - else if (res) - gfx_printf(&gfx_con, "\nFinished! Press any key...\n"); - -out: - sd_unmount(); - btn_wait(); -} - -void dump_emmc_system() { dump_emmc_selected(PART_SYSTEM); } -void dump_emmc_user() { dump_emmc_selected(PART_USER); } -void dump_emmc_boot() { dump_emmc_selected(PART_BOOT); } -void dump_emmc_rawnand() { dump_emmc_selected(PART_RAW); } - -int restore_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part) -{ - static const u32 SECTORS_TO_MIB_COEFF = 11; - - u32 totalSectors = part->lba_end - part->lba_start + 1; - u32 lbaStartPart = part->lba_start; - int res = 0; - char *outFilename = sd_path; - - gfx_con.fntsz = 8; - - FIL fp; - gfx_printf(&gfx_con, "\nFilename: %s\n", outFilename); - - res = f_open(&fp, outFilename, FA_READ); - if (res) - { - WPRINTFARGS("Error (%d) while opening backup. Continuing...\n", res); - gfx_con.fntsz = 16; - - return 0; - } - //TODO: Should we keep this check? - else if (((u32)((u64)f_size(&fp) >> (u64)9)) != totalSectors) - { - gfx_con.fntsz = 16; - EPRINTF("Size of the SD Card backup does not match,\neMMC's selected part size.\n"); - f_close(&fp); - - return 0; - } - else - gfx_printf(&gfx_con, "\nTotal restore size: %d MiB.\n\n", ((u32)((u64)f_size(&fp) >> (u64)9)) >> SECTORS_TO_MIB_COEFF); - - u32 numSectorsPerIter = 0; - if (totalSectors > 0x200000) - numSectorsPerIter = 8192; //4MB Cache - else - numSectorsPerIter = 512; //256KB Cache - - u8 *buf = (u8 *)calloc(numSectorsPerIter, NX_EMMC_BLOCKSIZE); - - u32 lba_curr = part->lba_start; - u32 bytesWritten = 0; - u32 prevPct = 200; - int retryCount = 0; - - u32 num = 0; - u32 pct = 0; - while (totalSectors > 0) - { - retryCount = 0; - num = MIN(totalSectors, numSectorsPerIter); - - res = f_read(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL); - if (res) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFatal error (%d) when reading from SD Card", res); - EPRINTF("\nYour device may be in an inoperative state!\n\nPress any key and try again now...\n"); - - free(buf); - f_close(&fp); - return 0; - } - while (!sdmmc_storage_write(storage, lba_curr, num, buf)) - { - EPRINTFARGS("Error writing %d blocks @ LBA %08X\nto eMMC (try %d), retrying...", - num, lba_curr, ++retryCount); - - msleep(150); - if (retryCount >= 3) - { - gfx_con.fntsz = 16; - EPRINTFARGS("\nFailed to write %d blocks @ LBA %08X\nfrom eMMC. Aborting..\n", - num, lba_curr); - EPRINTF("\nYour device may be in an inoperative state!\n\nPress any key and try again...\n"); - - free(buf); - f_close(&fp); - return 0; - } - } - pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); - if (pct != prevPct) - { - tui_pbar(&gfx_con, 0, gfx_con.y, pct, 0xFFCCCCCC, 0xFF555555); - prevPct = pct; - } - - lba_curr += num; - totalSectors -= num; - bytesWritten += num * NX_EMMC_BLOCKSIZE; - } - tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFFCCCCCC, 0xFF555555); - - // Restore operation ended successfully. - free(buf); - f_close(&fp); - - if (h_cfg.verification) - { - // Verify restored data. - if (dump_emmc_verify(storage, lbaStartPart, outFilename, part)) - { - EPRINTF("\nPress any key and try again...\n"); - - return 0; - } - else - tui_pbar(&gfx_con, 0, gfx_con.y, 100, 0xFF96FF00, 0xFF155500); - } - - gfx_con.fntsz = 16; - gfx_puts(&gfx_con, "\n\n"); - - return 1; -} - -static void restore_emmc_selected(emmcPartType_t restoreType) -{ - int res = 0; - u32 timer = 0; - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - tui_sbar(&gfx_con, true); - gfx_con_setpos(&gfx_con, 0, 0); - - gfx_printf(&gfx_con, "%kThis is a dangerous operation\nand may render your device inoperative!\n\n", 0xFFFFDD00); - gfx_printf(&gfx_con, "Are you really sure?\n\n%k", 0xFFCCCCCC); - if ((restoreType & PART_BOOT) || (restoreType & PART_GP_ALL)) - { - gfx_puts(&gfx_con, "The mode you selected will only restore\nthe "); - if (restoreType & PART_BOOT) - gfx_puts(&gfx_con, "boot "); - gfx_puts(&gfx_con, "partitions that it can find.\n"); - gfx_puts(&gfx_con, "If it is not found, it will be skipped\nand continue with the next.\n\n"); - } - gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); - - u8 value = 10; - while (value > 0) - { - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - gfx_printf(&gfx_con, "%kWait... (%ds) %k", 0xFF888888, value, 0xFFCCCCCC); - msleep(1000); - value--; - } - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - - gfx_puts(&gfx_con, "Press POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); - - u32 btn = btn_wait(); - if (!(btn & BTN_POWER)) - goto out; - - if (!sd_mount()) - goto out; - - sdmmc_storage_t storage; - sdmmc_t sdmmc; - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - goto out; - } - - int i = 0; - char sdPath[80]; - - timer = get_tmr_s(); - if (restoreType & PART_BOOT) - { - const u32 BOOT_PART_SIZE = storage.ext_csd.boot_mult << 17; - - emmc_part_t bootPart; - memset(&bootPart, 0, sizeof(bootPart)); - bootPart.lba_start = 0; - bootPart.lba_end = (BOOT_PART_SIZE / NX_EMMC_BLOCKSIZE) - 1; - for (i = 0; i < 2; i++) - { - memcpy(bootPart.name, "BOOT", 4); - bootPart.name[4] = (u8)('0' + i); - bootPart.name[5] = 0; - - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i, - bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFCCCCCC); - - sdmmc_storage_set_mmc_partition(&storage, i + 1); - - emmcsn_path_impl(sdPath, "/restore", bootPart.name, &storage); - res = restore_emmc_part(sdPath, &storage, &bootPart); - } - } - - if (restoreType & PART_GP_ALL) - { - sdmmc_storage_set_mmc_partition(&storage, 0); - - LIST_INIT(gpt); - nx_emmc_gpt_parse(&gpt, &storage); - LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) - { - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, - part->name, part->lba_start, part->lba_end, 0xFFCCCCCC); - - emmcsn_path_impl(sdPath, "/restore/partitions/", part->name, &storage); - res = restore_emmc_part(sdPath, &storage, part); - } - nx_emmc_gpt_free(&gpt); - } - - if (restoreType & PART_RAW) - { - // Get GP partition size dynamically. - const u32 RAW_AREA_NUM_SECTORS = storage.sec_cnt; - - emmc_part_t rawPart; - memset(&rawPart, 0, sizeof(rawPart)); - rawPart.lba_start = 0; - rawPart.lba_end = RAW_AREA_NUM_SECTORS - 1; - strcpy(rawPart.name, "rawnand.bin"); - { - gfx_printf(&gfx_con, "%k%02d: %s (%07X-%07X)%k\n", 0xFF00DDFF, i++, - rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFCCCCCC); - - emmcsn_path_impl(sdPath, "/restore", rawPart.name, &storage); - res = restore_emmc_part(sdPath, &storage, &rawPart); - } - } - - gfx_putc(&gfx_con, '\n'); - timer = get_tmr_s() - timer; - gfx_printf(&gfx_con, "Time taken: %dm %ds.\n", timer / 60, timer % 60); - sdmmc_storage_end(&storage); - if (res && h_cfg.verification) - gfx_printf(&gfx_con, "\n%kFinished and verified!%k\nPress any key...\n", 0xFF96FF00, 0xFFCCCCCC); - else if (res) - gfx_printf(&gfx_con, "\nFinished! Press any key...\n"); - -out: - sd_unmount(); - btn_wait(); -} - -void restore_emmc_boot() { restore_emmc_selected(PART_BOOT); } -void restore_emmc_rawnand() { restore_emmc_selected(PART_RAW); } -void restore_emmc_gpp_parts() { restore_emmc_selected(PART_GP_ALL); } - -void dump_packages12() -{ - if (!sd_mount()) - return; - - u8 *pkg1 = (u8 *)calloc(1, 0x40000); - u8 *warmboot = (u8 *)calloc(1, 0x40000); - u8 *secmon = (u8 *)calloc(1, 0x40000); - u8 *loader = (u8 *)calloc(1, 0x40000); - u8 *pkg2 = NULL; - - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - sdmmc_storage_t storage; - sdmmc_t sdmmc; - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - goto out_free; - } - sdmmc_storage_set_mmc_partition(&storage, 1); - - // Read package1. - sdmmc_storage_read(&storage, 0x100000 / NX_EMMC_BLOCKSIZE, 0x40000 / NX_EMMC_BLOCKSIZE, pkg1); - const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); - const pk11_hdr_t *hdr = (pk11_hdr_t *)(pkg1 + pkg1_id->pkg11_off + 0x20); - if (!pkg1_id) - { - gfx_con.fntsz = 8; - EPRINTFARGS("Unknown package1 version for reading\nTSEC firmware (= '%s').", (char *)pkg1 + 0x10); - goto out_free; - } - - if (!h_cfg.se_keygen_done) - { - // Read keyblob. - u8 *keyblob = (u8 *)calloc(NX_EMMC_BLOCKSIZE, 1); - sdmmc_storage_read(&storage, 0x180000 / NX_EMMC_BLOCKSIZE + pkg1_id->kb, 1, keyblob); - - // Decrypt. - keygen(keyblob, pkg1_id->kb, (u8 *)pkg1 + pkg1_id->tsec_off); - - h_cfg.se_keygen_done = 1; - free(keyblob); - } - pkg1_decrypt(pkg1_id, pkg1); - - pkg1_unpack(warmboot, secmon, loader, pkg1_id, pkg1); - - // Display info. - gfx_printf(&gfx_con, "%kNX Bootloader size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->ldr_size); - - gfx_printf(&gfx_con, "%kSecure monitor addr: %k0x%05X\n", 0xFFC7EA46, 0xFFCCCCCC, pkg1_id->secmon_base); - gfx_printf(&gfx_con, "%kSecure monitor size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->sm_size); - - gfx_printf(&gfx_con, "%kWarmboot addr: %k0x%05X\n", 0xFFC7EA46, 0xFFCCCCCC, pkg1_id->warmboot_base); - gfx_printf(&gfx_con, "%kWarmboot size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, hdr->wb_size); - - char path[64]; - // Dump package1.1. - emmcsn_path_impl(path, "/pkg1", "pkg1_decr.bin", &storage); - if (sd_save_to_file(pkg1, 0x40000, path)) - goto out_free; - gfx_puts(&gfx_con, "\nFull package1 dumped to pkg1_decr.bin\n"); - - // Dump nxbootloader. - emmcsn_path_impl(path, "/pkg1", "nxloader.bin", &storage); - if (sd_save_to_file(loader, hdr->ldr_size, path)) - goto out_free; - gfx_puts(&gfx_con, "NX Bootloader dumped to nxloader.bin\n"); - - // Dump secmon. - emmcsn_path_impl(path, "/pkg1", "secmon.bin", &storage); - if (sd_save_to_file(secmon, hdr->sm_size, path)) - goto out_free; - gfx_puts(&gfx_con, "Secure Monitor dumped to secmon.bin\n"); - - // Dump warmboot. - emmcsn_path_impl(path, "/pkg1", "warmboot.bin", &storage); - if (sd_save_to_file(warmboot, hdr->wb_size, path)) - goto out_free; - gfx_puts(&gfx_con, "Warmboot dumped to warmboot.bin\n\n\n"); - - // Dump package2.1. - sdmmc_storage_set_mmc_partition(&storage, 0); - // Parse eMMC GPT. - LIST_INIT(gpt); - nx_emmc_gpt_parse(&gpt, &storage); - // Find package2 partition. - emmc_part_t *pkg2_part = nx_emmc_part_find(&gpt, "BCPKG2-1-Normal-Main"); - if (!pkg2_part) - goto out; - - // Read in package2 header and get package2 real size. - u8 *tmp = (u8 *)malloc(NX_EMMC_BLOCKSIZE); - nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, 1, tmp); - u32 *hdr_pkg2_raw = (u32 *)(tmp + 0x100); - u32 pkg2_size = hdr_pkg2_raw[0] ^ hdr_pkg2_raw[2] ^ hdr_pkg2_raw[3]; - free(tmp); - // Read in package2. - u32 pkg2_size_aligned = ALIGN(pkg2_size, NX_EMMC_BLOCKSIZE); - pkg2 = malloc(pkg2_size_aligned); - nx_emmc_part_read(&storage, pkg2_part, 0x4000 / NX_EMMC_BLOCKSIZE, - pkg2_size_aligned / NX_EMMC_BLOCKSIZE, pkg2); - // Decrypt package2 and parse KIP1 blobs in INI1 section. - pkg2_hdr_t *pkg2_hdr = pkg2_decrypt(pkg2); - - // Display info. - u32 kernel_crc32 = crc32c(pkg2_hdr->data, pkg2_hdr->sec_size[PKG2_SEC_KERNEL]); - gfx_printf(&gfx_con, "\n%kKernel CRC32C: %k0x%08X\n\n", 0xFFC7EA46, 0xFFCCCCCC, kernel_crc32); - gfx_printf(&gfx_con, "%kKernel size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, pkg2_hdr->sec_size[PKG2_SEC_KERNEL]); - gfx_printf(&gfx_con, "%kINI1 size: %k0x%05X\n\n", 0xFFC7EA46, 0xFFCCCCCC, pkg2_hdr->sec_size[PKG2_SEC_INI1]); - - // Dump pkg2.1. - emmcsn_path_impl(path, "/pkg2", "pkg2_decr.bin", &storage); - if (sd_save_to_file(pkg2, pkg2_hdr->sec_size[PKG2_SEC_KERNEL] + pkg2_hdr->sec_size[PKG2_SEC_INI1], path)) - goto out; - gfx_puts(&gfx_con, "\nFull package2 dumped to pkg2_decr.bin\n"); - - // Dump kernel. - emmcsn_path_impl(path, "/pkg2", "kernel.bin", &storage); - if (sd_save_to_file(pkg2_hdr->data, pkg2_hdr->sec_size[PKG2_SEC_KERNEL], path)) - goto out; - gfx_puts(&gfx_con, "Kernel dumped to kernel.bin\n"); - - // Dump INI1. - emmcsn_path_impl(path, "/pkg2", "ini1.bin", &storage); - if (sd_save_to_file(pkg2_hdr->data + pkg2_hdr->sec_size[PKG2_SEC_KERNEL], - pkg2_hdr->sec_size[PKG2_SEC_INI1], path)) - goto out; - gfx_puts(&gfx_con, "INI1 kip1 package dumped to ini1.bin\n"); - - gfx_puts(&gfx_con, "\nDone. Press any key...\n"); - -out: - nx_emmc_gpt_free(&gpt); -out_free: - free(pkg1); - free(secmon); - free(warmboot); - free(loader); - free(pkg2); - sdmmc_storage_end(&storage); - sd_unmount(); - - btn_wait(); -} - // This is a safe and unused DRAM region for our payloads. -#define IPL_START 0x40008000 -#define EXT_PAYLOAD_ADDR 0xC03C0000 -#define PATCHED_RELOC_SZ 0x94 -#define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10)) -#define PAYLOAD_ENTRY 0x40010000 +#define IPL_LOAD_ADDR 0x40008000 +#define EXT_PAYLOAD_ADDR 0xC03C0000 +#define PATCHED_RELOC_SZ 0x94 +#define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10)) +#define PAYLOAD_ENTRY 0x40010000 #define CBFS_SDRAM_EN_ADDR 0x4003e000 -#define COREBOOT_ADDR (0xD0000000 - 0x100000) +#define COREBOOT_ADDR (0xD0000000 - 0x100000) void (*ext_payload_ptr)() = (void *)EXT_PAYLOAD_ADDR; @@ -1848,7 +276,7 @@ void reloc_patcher(u32 payload_size) static const u32 PAYLOAD_END_OFF = 0x84; static const u32 IPL_START_OFF = 0x88; - memcpy((u8 *)EXT_PAYLOAD_ADDR, (u8 *)IPL_START, PATCHED_RELOC_SZ); + memcpy((u8 *)EXT_PAYLOAD_ADDR, (u8 *)IPL_LOAD_ADDR, PATCHED_RELOC_SZ); *(vu32 *)(EXT_PAYLOAD_ADDR + START_OFF) = PAYLOAD_ENTRY - ALIGN(PATCHED_RELOC_SZ, 0x10); *(vu32 *)(EXT_PAYLOAD_ADDR + PAYLOAD_END_OFF) = PAYLOAD_ENTRY + payload_size; @@ -1861,6 +289,9 @@ void reloc_patcher(u32 payload_size) } } +#define BOOTLOADER_UPDATED_MAGIC 0x424f4f54 // "BOOT". +#define BOOTLOADER_UPDATED_MAGIC_ADDR 0x4003E000 + int launch_payload(char *path, bool update) { gfx_clear_grey(&gfx_ctxt, 0x1B); @@ -2538,628 +969,6 @@ out: gfx_con.mute = false; } -void toggle_autorcm(bool enable) -{ - sdmmc_storage_t storage; - sdmmc_t sdmmc; - - u8 randomXor = 0; - - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - goto out; - } - - u8 *tempbuf = (u8 *)malloc(0x200); - sdmmc_storage_set_mmc_partition(&storage, 1); - - int i, sect = 0; - for (i = 0; i < 4; i++) - { - sect = (0x200 + (0x4000 * i)) / NX_EMMC_BLOCKSIZE; - sdmmc_storage_read(&storage, sect, 1, tempbuf); - - if (enable) - { - do - { - randomXor = get_tmr_us() & 0xFF; // Bricmii style of bricking. - } while (!randomXor); // Avoid the lottery. - - tempbuf[0x10] ^= randomXor; - } - else - tempbuf[0x10] = 0xF7; - sdmmc_storage_write(&storage, sect, 1, tempbuf); - } - - free(tempbuf); - sdmmc_storage_end(&storage); - - if (enable) - gfx_printf(&gfx_con, "%kAutoRCM mode enabled!%k", 0xFFFFBA00, 0xFFCCCCCC); - else - gfx_printf(&gfx_con, "%kAutoRCM mode disabled!%k", 0xFF96FF00, 0xFFCCCCCC); - gfx_printf(&gfx_con, "\n\nPress any key...\n"); - -out: - btn_wait(); -} - -void enable_autorcm() { toggle_autorcm(true); } -void disable_autorcm() { toggle_autorcm(false); } - -void menu_autorcm() -{ - gfx_clear_grey(&gfx_ctxt, 0x1B); - gfx_con_setpos(&gfx_con, 0, 0); - - // Do a simple check on the main BCT. - sdmmc_storage_t storage; - sdmmc_t sdmmc; - bool disabled = true; - - if (!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) - { - EPRINTF("Failed to init eMMC."); - btn_wait(); - - return; - } - - u8 *tempbuf = (u8 *)malloc(0x200); - sdmmc_storage_set_mmc_partition(&storage, 1); - sdmmc_storage_read(&storage, 0x200 / NX_EMMC_BLOCKSIZE, 1, tempbuf); - - if (tempbuf[0x10] != 0xF7) - disabled = false; - - free(tempbuf); - sdmmc_storage_end(&storage); - - // Create AutoRCM menu. - ment_t *ments = (ment_t *)malloc(sizeof(ment_t) * 6); - - ments[0].type = MENT_BACK; - ments[0].caption = "Back"; - - ments[1].type = MENT_CHGLINE; - - ments[2].type = MENT_CAPTION; - ments[3].type = MENT_CHGLINE; - if (disabled) - { - ments[2].caption = "Status: Disabled!"; - ments[2].color = 0xFF96FF00; - ments[4].caption = "Enable AutoRCM"; - ments[4].handler = enable_autorcm; - } - else - { - ments[2].caption = "Status: Enabled!"; - ments[2].color = 0xFFFFBA00; - ments[4].caption = "Disable AutoRCM"; - ments[4].handler = disable_autorcm; - } - ments[4].type = MENT_HDLR_RE; - - memset(&ments[5], 0, sizeof(ment_t)); - menu_t menu = {ments, "This corrupts your BOOT0!", 0, 0}; - - tui_do_menu(&gfx_con, &menu); -} - -int fix_attributes(char *path, u32 *total, u32 is_root, u32 check_first_run) -{ - FRESULT res; - DIR dir; - u32 dirLength = 0; - static FILINFO fno; - - if (check_first_run) - { - // Read file attributes. - res = f_stat(path, &fno); - if (res != FR_OK) - return res; - - // Check if archive bit is set. - if (fno.fattrib & AM_ARC) - { - *(u32 *)total = *(u32 *)total + 1; - f_chmod(path, 0, AM_ARC); - } - } - - // Open directory. - res = f_opendir(&dir, path); - if (res != FR_OK) - return res; - - dirLength = strlen(path); - for (;;) - { - // Clear file or folder path. - path[dirLength] = 0; - - // Read a directory item. - res = f_readdir(&dir, &fno); - - // Break on error or end of dir. - if (res != FR_OK || fno.fname[0] == 0) - break; - - // Skip official Nintendo dir. - if (is_root && !strcmp(fno.fname, "Nintendo")) - continue; - - // Set new directory or file. - memcpy(&path[dirLength], "/", 1); - memcpy(&path[dirLength + 1], fno.fname, strlen(fno.fname) + 1); - - // Check if archive bit is set. - if (fno.fattrib & AM_ARC) - { - *(u32 *)total = *(u32 *)total + 1; - f_chmod(path, 0, AM_ARC); - } - - // Is it a directory? - if (fno.fattrib & AM_DIR) - { - // Enter the directory. - res = fix_attributes(path, total, 0, 0); - if (res != FR_OK) - break; - } - } - - f_closedir(&dir); - - return res; -} - -void fix_sd_attr(u32 type) -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - char path[256]; - char label[14]; - - u32 total = 0; - if (sd_mount()) - { - switch (type) - { - case 0: - memcpy(path, "/", 2); - memcpy(label, "SD Card", 8); - break; - case 1: - default: - memcpy(path, "/switch", 8); - memcpy(label, "switch folder", 14); - break; - } - - gfx_printf(&gfx_con, "Traversing all %s files!\nThis may take some time, please wait...\n\n", label); - fix_attributes(path, &total, !type, type); - gfx_printf(&gfx_con, "%kTotal archive bits cleared: %d!%k\n\nDone! Press any key...", 0xFF96FF00, total, 0xFFCCCCCC); - sd_unmount(); - } - btn_wait(); -} - -void fix_sd_all_attr() { fix_sd_attr(0); } -void fix_sd_switch_attr() { fix_sd_attr(1); } - -void print_fuel_gauge_info() -{ - int value = 0; - - gfx_printf(&gfx_con, "%kFuel Gauge IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); - - max17050_get_property(MAX17050_Age, &value); - gfx_printf(&gfx_con, "Age: %3d%\n", value); - - max17050_get_property(MAX17050_RepSOC, &value); - gfx_printf(&gfx_con, "Capacity now: %3d%\n", value >> 8); - - max17050_get_property(MAX17050_RepCap, &value); - gfx_printf(&gfx_con, "Capacity now: %4d mAh\n", value); - - max17050_get_property(MAX17050_FullCAP, &value); - gfx_printf(&gfx_con, "Capacity full: %4d mAh\n", value); - - max17050_get_property(MAX17050_DesignCap, &value); - gfx_printf(&gfx_con, "Capacity (design): %4d mAh\n", value); - - max17050_get_property(MAX17050_Current, &value); - if (value >= 0) - gfx_printf(&gfx_con, "Current now: %d mA\n", value / 1000); - else - gfx_printf(&gfx_con, "Current now: -%d mA\n", ~value / 1000); - - max17050_get_property(MAX17050_AvgCurrent, &value); - if (value >= 0) - gfx_printf(&gfx_con, "Current average: %d mA\n", value / 1000); - else - gfx_printf(&gfx_con, "Current average: -%d mA\n", ~value / 1000); - - max17050_get_property(MAX17050_VCELL, &value); - gfx_printf(&gfx_con, "Voltage now: %4d mV\n", value); - - max17050_get_property(MAX17050_OCVInternal, &value); - gfx_printf(&gfx_con, "Voltage open-circuit: %4d mV\n", value); - - max17050_get_property(MAX17050_MinVolt, &value); - gfx_printf(&gfx_con, "Min voltage reached: %4d mV\n", value); - - max17050_get_property(MAX17050_MaxVolt, &value); - gfx_printf(&gfx_con, "Max voltage reached: %4d mV\n", value); - - max17050_get_property(MAX17050_V_empty, &value); - gfx_printf(&gfx_con, "Empty voltage (design): %4d mV\n", value); - - max17050_get_property(MAX17050_TEMP, &value); - if (value >= 0) - gfx_printf(&gfx_con, "Battery temperature: %d.%d oC\n", value / 10, value % 10); - else - gfx_printf(&gfx_con, "Battery temperature: -%d.%d oC\n", ~value / 10, (~value) % 10); -} - -void print_battery_charger_info() -{ - int value = 0; - - gfx_printf(&gfx_con, "%k\n\nBattery Charger IC Info:\n%k", 0xFF00DDFF, 0xFFCCCCCC); - - bq24193_get_property(BQ24193_InputVoltageLimit, &value); - gfx_printf(&gfx_con, "Input voltage limit: %4d mV\n", value); - - bq24193_get_property(BQ24193_InputCurrentLimit, &value); - gfx_printf(&gfx_con, "Input current limit: %4d mA\n", value); - - bq24193_get_property(BQ24193_SystemMinimumVoltage, &value); - gfx_printf(&gfx_con, "Min voltage limit: %4d mV\n", value); - - bq24193_get_property(BQ24193_FastChargeCurrentLimit, &value); - gfx_printf(&gfx_con, "Fast charge current limit: %4d mA\n", value); - - bq24193_get_property(BQ24193_ChargeVoltageLimit, &value); - gfx_printf(&gfx_con, "Charge voltage limit: %4d mV\n", value); - - bq24193_get_property(BQ24193_ChargeStatus, &value); - gfx_printf(&gfx_con, "Charge status: "); - switch (value) - { - case 0: - gfx_printf(&gfx_con, "Not charging\n"); - break; - case 1: - gfx_printf(&gfx_con, "Pre-charging\n"); - break; - case 2: - gfx_printf(&gfx_con, "Fast charging\n"); - break; - case 3: - gfx_printf(&gfx_con, "Charge terminated\n"); - break; - default: - gfx_printf(&gfx_con, "Unknown (%d)\n", value); - break; - } - bq24193_get_property(BQ24193_TempStatus, &value); - gfx_printf(&gfx_con, "Temperature status: "); - switch (value) - { - case 0: - gfx_printf(&gfx_con, "Normal\n"); - break; - case 2: - gfx_printf(&gfx_con, "Warm\n"); - break; - case 3: - gfx_printf(&gfx_con, "Cool\n"); - break; - case 5: - gfx_printf(&gfx_con, "Cold\n"); - break; - case 6: - gfx_printf(&gfx_con, "Hot\n"); - break; - default: - gfx_printf(&gfx_con, "Unknown (%d)\n", value); - break; - } -} - -void print_battery_info() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - print_fuel_gauge_info(); - - print_battery_charger_info(); - - u8 *buf = (u8 *)malloc(0x100 * 2); - - gfx_printf(&gfx_con, "%k\n\nBattery Fuel Gauge Registers:\n%k", 0xFF00DDFF, 0xFFCCCCCC); - - for (int i = 0; i < 0x200; i += 2) - { - i2c_recv_buf_small(buf + i, 2, I2C_1, 0x36, i >> 1); - usleep(2500); - } - - gfx_hexdump(&gfx_con, 0, (u8 *)buf, 0x200); - - gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); - - 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 - gfx_puts(&gfx_con, "\nDone!\n"); - sd_unmount(); - } - - btn_wait(); - } - free(buf); -} - -/* void fix_fuel_gauge_configuration() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - int battVoltage, avgCurrent; - - max17050_get_property(MAX17050_VCELL, &battVoltage); - max17050_get_property(MAX17050_AvgCurrent, &avgCurrent); - - // Check if still charging. If not, check if battery is >= 95% (4.1V). - if (avgCurrent < 0 && battVoltage > 4100) - { - if ((avgCurrent / 1000) < -10) - EPRINTF("You need to be connected to a wall adapter,\nto apply this fix!"); - else - { - gfx_printf(&gfx_con, "%kAre you really sure?\nThis will reset your fuel gauge completely!\n", 0xFFFFDD00); - gfx_printf(&gfx_con, "Additionally this will power off your console.\n%k", 0xFFCCCCCC); - - gfx_puts(&gfx_con, "\nPress POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); - - u32 btn = btn_wait(); - if (btn & BTN_POWER) - { - max17050_fix_configuration(); - msleep(1000); - gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); - u16 value = 0; - gfx_printf(&gfx_con, "%kThe console will power off in 45 seconds.\n%k", 0xFFFFDD00, 0xFFCCCCCC); - while (value < 46) - { - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - gfx_printf(&gfx_con, "%2ds elapsed", value); - msleep(1000); - value++; - } - msleep(2000); - - power_off(); - } - return; - } - } - else - EPRINTF("You need a fully charged battery\nand connected to a wall adapter,\nto apply this fix!"); - - msleep(500); - btn_wait(); -} */ - -/*void reset_pmic_fuel_gauge_charger_config() -{ - int avgCurrent; - - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - gfx_printf(&gfx_con, "%k\nThis will wipe your battery stats completely!\n" - "%kAnd it may not power on without physically\nremoving and re-inserting the battery.\n%k" - "\nAre you really sure?%k\n", 0xFFFFDD00, 0xFFFF0000, 0xFFFFDD00, 0xFFCCCCCC); - - gfx_puts(&gfx_con, "\nPress POWER to Continue.\nPress VOL to go to the menu.\n\n\n"); - u32 btn = btn_wait(); - if (btn & BTN_POWER) - { - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - gfx_printf(&gfx_con, "%kKeep the USB cable connected!%k\n\n", 0xFFFFDD00, 0xFFCCCCCC); - gfx_con_getpos(&gfx_con, &gfx_con.savedx, &gfx_con.savedy); - - u8 value = 30; - while (value > 0) - { - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - gfx_printf(&gfx_con, "%kWait... (%ds) %k", 0xFF888888, value, 0xFFCCCCCC); - msleep(1000); - value--; - } - gfx_con_setpos(&gfx_con, gfx_con.savedx, gfx_con.savedy); - - //Check if still connected. - max17050_get_property(MAX17050_AvgCurrent, &avgCurrent); - if ((avgCurrent / 1000) < -10) - EPRINTF("You need to be connected to a wall adapter\nor PC to apply this fix!"); - else - { - // Apply fix. - bq24193_fake_battery_removal(); - gfx_printf(&gfx_con, "Done! \n" - "%k1. Remove the USB cable\n" - "2. Press POWER for 15s.\n" - "3. Reconnect the USB to power-on!%k\n", 0xFFFFDD00, 0xFFCCCCCC); - } - msleep(500); - btn_wait(); - } -}*/ - -void fix_battery_desync() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - max77620_low_battery_monitor_config(); - - gfx_puts(&gfx_con, "\nDone!\n"); - - btn_wait(); -} - -void ipatch_process(u32 offset, u32 value) -{ - gfx_printf(&gfx_con, "%8x %8x", BOOTROM_BASE + offset, value); - u8 lo = value & 0xff; - switch (value >> 8) - { - case 0xdf: - gfx_printf(&gfx_con, " svc #0x%02x", lo); - break; - case 0x20: - gfx_printf(&gfx_con, " movs r0, #0x%02x", lo); - break; - } - gfx_puts(&gfx_con, "\n"); -} - -void bootrom_ipatches_info() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - static const u32 BOOTROM_SIZE = 0x18000; - - u32 res = fuse_read_ipatch(ipatch_process); - if (res != 0) - EPRINTFARGS("Failed to read ipatches. Error: %d", res); - - gfx_puts(&gfx_con, "\nPress POWER to dump them to SD Card.\nPress VOL to go to the menu.\n"); - - 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)) - gfx_puts(&gfx_con, "\nevp_thunks.bin saved!\n"); - } - else - EPRINTFARGS("Failed to read evp_thunks. Error: %d", res); - - u32 words[0x100]; - read_raw_ipatch_fuses(words); - emmcsn_path_impl(path, "/dumps", "ipatches.bin", NULL); - if (!sd_save_to_file((u8 *)words, sizeof(words), path)) - gfx_puts(&gfx_con, "\nipatches.bin saved!\n"); - - emmcsn_path_impl(path, "/dumps", "bootrom_patched.bin", NULL); - if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) - gfx_puts(&gfx_con, "\nbootrom_patched.bin saved!\n"); - - u8 ipatch_backup[13]; - memcpy(ipatch_backup, (void *) IPATCH_BASE, 13); - memset((void*)IPATCH_BASE, 0, 13); - - emmcsn_path_impl(path, "/dumps", "bootrom_unpatched.bin", NULL); - if (!sd_save_to_file((u8 *)BOOTROM_BASE, BOOTROM_SIZE, path)) - gfx_puts(&gfx_con, "\nbootrom_unpatched.bin saved!\n"); - - memcpy((void*)IPATCH_BASE, ipatch_backup, 13); - - sd_unmount(); - } - - btn_wait(); - } -} - -/* -//#include "../modules/hekate_libsys_minerva/mtc.h" -//mtc_config_t mtc_cfg; - -void minerva() -{ - gfx_clear_partial_grey(&gfx_ctxt, 0x1B, 0, 1256); - gfx_con_setpos(&gfx_con, 0, 0); - - u32 curr_ram_idx = 0; - - if (!sd_mount()) - return; - - gfx_printf(&gfx_con, "-- Minerva Training Cell --\n\n"); - - // Set table to ram. - mtc_cfg.mtc_table = NULL; - mtc_cfg.sdram_id = (fuse_read_odm(4) >> 3) & 0x1F; - ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); - - gfx_printf(&gfx_con, "\nStarting training process..\n\n"); - - // Get current frequency - for (curr_ram_idx = 0; curr_ram_idx < 10; curr_ram_idx++) - { - if (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_EMC) == mtc_cfg.mtc_table[curr_ram_idx].clk_src_emc) - break; - } - - mtc_cfg.rate_from = mtc_cfg.mtc_table[curr_ram_idx].rate_khz; - mtc_cfg.rate_to = 800000; - mtc_cfg.train_mode = OP_TRAIN_SWITCH; - gfx_printf(&gfx_con, "Training and switching %7d -> %7d\n\n", mtc_cfg.mtc_table[curr_ram_idx].rate_khz, 800000); - ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); - - // Thefollowing frequency needs periodic training every 100ms. - //msleep(200); - - //mtc_cfg.rate_to = 1600000; - //gfx_printf(&gfx_con, "Training and switching %7d -> %7d\n\n", mtc_cfg.current_emc_table->rate_khz, 1600000); - //ianos_loader(false, "bootloader/sys/libsys_minerva.bso", DRAM_LIB, (void *)&mtc_cfg); - - //mtc_cfg.train_mode = OP_PERIODIC_TRAIN; - - sd_unmount(); - - gfx_printf(&gfx_con, "Finished!"); - - btn_wait(); -} -*/ - void about() { static const char credits[] = diff --git a/bootloader/soc/hw_init.c b/bootloader/soc/hw_init.c new file mode 100644 index 0000000..5c42219 --- /dev/null +++ b/bootloader/soc/hw_init.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * + * 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 . + */ + +#include + +#include "hw_init.h" +#include "../gfx/di.h" +#include "../mem/mc.h" +#include "../mem/sdram.h" +#include "../power/max77620.h" +#include "../power/max7762x.h" +#include "../sec/se.h" +#include "../sec/se_t210.h" +#include "../soc/clock.h" +#include "../soc/fuse.h" +#include "../soc/gpio.h" +#include "../soc/i2c.h" +#include "../soc/pinmux.h" +#include "../soc/pmc.h" +#include "../soc/t210.h" +#include "../soc/uart.h" +#include "../storage/sdmmc.h" +#include "../utils/util.h" + +extern sdmmc_t sd_sdmmc; + +void _config_oscillators() +{ + CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) = (CLOCK(CLK_RST_CONTROLLER_SPARE_REG0) & 0xFFFFFFF3) | 4; + SYSCTR0(SYSCTR0_CNTFID0) = 19200000; + TMR(TIMERUS_USEC_CFG) = 0x45F; // For 19.2MHz clk_m. + CLOCK(CLK_RST_CONTROLLER_OSC_CTRL) = 0x50000071; + PMC(APBDEV_PMC_OSC_EDPD_OVER) = (PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFFFFF81) | 0xE; + PMC(APBDEV_PMC_OSC_EDPD_OVER) = (PMC(APBDEV_PMC_OSC_EDPD_OVER) & 0xFFBFFFFF) | 0x400000; + PMC(APBDEV_PMC_CNTRL2) = (PMC(APBDEV_PMC_CNTRL2) & 0xFFFFEFFF) | 0x1000; + PMC(APBDEV_PMC_SCRATCH188) = (PMC(APBDEV_PMC_SCRATCH188) & 0xFCFFFFFF) | 0x2000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 0x10; + CLOCK(CLK_RST_CONTROLLER_PLLMB_BASE) &= 0xBFFFFFFF; + PMC(APBDEV_PMC_TSC_MULT) = (PMC(APBDEV_PMC_TSC_MULT) & 0xFFFF0000) | 0x249F; //0x249F = 19200000 * (16 / 32.768 kHz) + CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = 0x20004444; + CLOCK(CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER) = 0x80000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE) = 2; +} + +void _config_gpios() +{ + PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0; + PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0; + + PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE; + PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE; + + gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_G, GPIO_PIN_0, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_D, GPIO_PIN_1, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_E, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_H, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + + pinmux_config_i2c(I2C_1); + pinmux_config_i2c(I2C_5); + pinmux_config_uart(UART_A); + + // Configure volume up/down as inputs. + gpio_config(GPIO_PORT_X, GPIO_PIN_6, GPIO_MODE_GPIO); + gpio_config(GPIO_PORT_X, GPIO_PIN_7, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_X, GPIO_PIN_6, GPIO_OUTPUT_DISABLE); + gpio_output_enable(GPIO_PORT_X, GPIO_PIN_7, GPIO_OUTPUT_DISABLE); +} + +void _config_pmc_scratch() +{ + PMC(APBDEV_PMC_SCRATCH20) &= 0xFFF3FFFF; + PMC(APBDEV_PMC_SCRATCH190) &= 0xFFFFFFFE; + PMC(APBDEV_PMC_SECURE_SCRATCH21) |= 0x10; +} + +void _mbist_workaround() +{ + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) | 0x8000) & 0xFFFFBFFF; + CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) |= 0x40800000u; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_Y_CLR) = 0x40; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_X_CLR) = 0x40000; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = 0x18000000; + usleep(2); + + I2S(I2S1_CTRL) |= I2S_CTRL_MASTER_EN; + I2S(I2S1_CG) &= ~I2S_CG_SLCG_ENABLE; + I2S(I2S2_CTRL) |= I2S_CTRL_MASTER_EN; + I2S(I2S2_CG) &= ~I2S_CG_SLCG_ENABLE; + I2S(I2S3_CTRL) |= I2S_CTRL_MASTER_EN; + I2S(I2S3_CG) &= ~I2S_CG_SLCG_ENABLE; + I2S(I2S4_CTRL) |= I2S_CTRL_MASTER_EN; + I2S(I2S4_CG) &= ~I2S_CG_SLCG_ENABLE; + I2S(I2S5_CTRL) |= I2S_CTRL_MASTER_EN; + I2S(I2S5_CG) &= ~I2S_CG_SLCG_ENABLE; + DISPLAY_A(_DIREG(DC_COM_DSC_TOP_CTL)) |= 4; + VIC(0x8C) = 0xFFFFFFFF; + usleep(2); + + CLOCK(CLK_RST_CONTROLLER_RST_DEV_Y_SET) = 0x40; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = 0x18000000; + CLOCK(CLK_RST_CONTROLLER_RST_DEV_X_SET) = 0x40000; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_H) = 0xC0; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) = 0x80000130; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) = 0x1F00200; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) = 0x80400808; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_W) = 0x402000FC; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X) = 0x23000780; + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) = 0x300; + CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA) = 0; + CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB) = 0; + CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC) = 0; + CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD) = 0; + CLOCK(CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE) = 0; + CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) &= 0x1F7FFFFF; + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SOR1) &= 0xFFFF3FFF; + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_VI) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_VI) & 0x1FFFFFFF) | 0x80000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X) & 0x1FFFFFFF) | 0x80000000; + CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_NVENC) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_NVENC) & 0x1FFFFFFF) | 0x80000000; +} + +void _config_se_brom() +{ + // Bootrom part we skipped. + u32 sbk[4] = { + FUSE(FUSE_PRIVATE_KEY0), + FUSE(FUSE_PRIVATE_KEY1), + FUSE(FUSE_PRIVATE_KEY2), + FUSE(FUSE_PRIVATE_KEY3) + }; + // Set SBK to slot 14. + se_aes_key_set(14, sbk, 0x10); + + // Lock SBK from being read. + SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 14 * 4) = 0x7E; + + // This memset needs to happen here, else TZRAM will behave weirdly later on. + memset((void *)TZRAM_BASE, 0, 0x10000); + PMC(APBDEV_PMC_CRYPTO_OP) = 0; + SE(SE_INT_STATUS_REG_OFFSET) = 0x1F; + + // Lock SSK (although it's not set and unused anyways). + SE(SE_KEY_TABLE_ACCESS_REG_OFFSET + 15 * 4) = 0x7E; + + // Clear the boot reason to avoid problems later + PMC(APBDEV_PMC_SCRATCH200) = 0x0; + PMC(APBDEV_PMC_RST_STATUS) = 0x0; + APB_MISC(APB_MISC_PP_STRAPPING_OPT_A) |= (7 << 10); +} + +void config_hw() +{ + // Bootrom stuff we skipped by going through rcm. + _config_se_brom(); + //FUSE(FUSE_PRIVATEKEYDISABLE) = 0x11; + SYSREG(AHB_AHB_SPARE_REG) &= 0xFFFFFF9F; + PMC(APBDEV_PMC_SCRATCH49) = ((PMC(APBDEV_PMC_SCRATCH49) >> 1) << 1) & 0xFFFFFFFD; + + _mbist_workaround(); + clock_enable_se(); + + // Enable fuse clock. + clock_enable_fuse(true); + // Disable fuse programming. + fuse_disable_program(); + + mc_enable(); + + _config_oscillators(); + APB_MISC(APB_MISC_PP_PINMUX_GLOBAL) = 0; + _config_gpios(); + + //clock_enable_uart(UART_C); + //uart_init(UART_C, 115200); + + clock_enable_cl_dvfs(); + + clock_enable_i2c(I2C_1); + clock_enable_i2c(I2C_5); + + clock_enable_unk2(); + + i2c_init(I2C_1); + i2c_init(I2C_5); + + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CNFGBBC, 0x40); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, 0x78); + + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG0, 0x38); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG1, 0x3A); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_CFG2, 0x38); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_LDO4, 0xF); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_LDO8, 0xC7); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD0, 0x4F); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD1, 0x29); + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_SD3, 0x1B); + + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_FPS_GPIO3, 0x22); // 3.x+ + + i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD0, 42); //42 = (1125000uV - 600000) / 12500 -> 1.125V + + _config_pmc_scratch(); // Missing from 4.x+ + + CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) = (CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY) & 0xFFFF8888) | 0x3333; + + sdram_init(); +} + +void reconfig_hw_workaround(bool extra_reconfig, u32 magic) +{ + // Re-enable clocks to Audio Processing Engine as a workaround to hanging. + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_V) |= (1 << 10); // Enable AHUB clock. + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) |= (1 << 6); // Enable APE clock. + + if (extra_reconfig) + { + msleep(10); + PMC(APBDEV_PMC_PWR_DET_VAL) |= PMC_PWR_DET_SDMMC1_IO_EN; + + clock_disable_cl_dvfs(); + + // Disable Joy-con GPIOs. + gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); + gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); + gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_SPIO); + gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_SPIO); + } + + // Power off display. + display_end(); + + // Enable clock to USBD and init SDMMC1 to avoid hangs with bad hw inits. + if (magic == 0xBAADF00D) + { + CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) |= (1 << 22); + sdmmc_init(&sd_sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0); + clock_disable_cl_dvfs(); + + msleep(200); + } +} \ No newline at end of file diff --git a/bootloader/soc/hw_init.h b/bootloader/soc/hw_init.h new file mode 100644 index 0000000..b92814b --- /dev/null +++ b/bootloader/soc/hw_init.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 naehrwert + * Copyright (c) 2018 CTCaer + * + * 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 . + */ + +#ifndef _HW_INIT_H_ +#define _HW_INIT_H_ + +#include "../utils/types.h" + +void config_hw(); +void reconfig_hw_workaround(bool extra_reconfig, u32 magic); + +#endif diff --git a/bootloader/soc/t210.h b/bootloader/soc/t210.h index bd7e4f9..267f96b 100644 --- a/bootloader/soc/t210.h +++ b/bootloader/soc/t210.h @@ -124,6 +124,9 @@ #define APBDEV_RTC_SHADOW_SECONDS 0xC #define APBDEV_RTC_MILLI_SECONDS 0x10 +/*! SYSCTR0 registers. */ +#define SYSCTR0_CNTFID0 0x20 + /*! TMR registers. */ #define TIMERUS_CNTR_1US (0x10 + 0x0) #define TIMERUS_USEC_CFG (0x10 + 0x4)