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)