mirror of
https://github.com/CTCaer/hekate.git
synced 2024-09-03 05:19:18 +00:00
Major refactoring of main.c
This commit is contained in:
parent
267a04c4ac
commit
471c1e514c
4
Makefile
4
Makefile
@ -15,6 +15,9 @@ VPATH = $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*
|
|||||||
OBJS = $(addprefix $(BUILD)/$(TARGET)/, \
|
OBJS = $(addprefix $(BUILD)/$(TARGET)/, \
|
||||||
start.o \
|
start.o \
|
||||||
main.o \
|
main.o \
|
||||||
|
fe_emmc_tools.o \
|
||||||
|
fe_info.o \
|
||||||
|
fe_tools.o \
|
||||||
config.o \
|
config.o \
|
||||||
btn.o \
|
btn.o \
|
||||||
clock.o \
|
clock.o \
|
||||||
@ -43,6 +46,7 @@ OBJS = $(addprefix $(BUILD)/$(TARGET)/, \
|
|||||||
se.o \
|
se.o \
|
||||||
tsec.o \
|
tsec.o \
|
||||||
uart.o \
|
uart.o \
|
||||||
|
hw_init.o \
|
||||||
dirlist.o \
|
dirlist.o \
|
||||||
ini.o \
|
ini.o \
|
||||||
ianos.o \
|
ianos.o \
|
||||||
|
884
bootloader/frontend/fe_emmc_tools.c
Normal file
884
bootloader/frontend/fe_emmc_tools.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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); }
|
30
bootloader/frontend/fe_emmc_tools.h
Normal file
30
bootloader/frontend/fe_emmc_tools.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
644
bootloader/frontend/fe_info.c
Normal file
644
bootloader/frontend/fe_info.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
}
|
32
bootloader/frontend/fe_info.h
Normal file
32
bootloader/frontend/fe_info.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
582
bootloader/frontend/fe_tools.c
Normal file
582
bootloader/frontend/fe_tools.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
*/
|
27
bootloader/frontend/fe_tools.h
Normal file
27
bootloader/frontend/fe_tools.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
2247
bootloader/main.c
2247
bootloader/main.c
File diff suppressed because it is too large
Load Diff
258
bootloader/soc/hw_init.c
Normal file
258
bootloader/soc/hw_init.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
26
bootloader/soc/hw_init.h
Normal file
26
bootloader/soc/hw_init.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
@ -124,6 +124,9 @@
|
|||||||
#define APBDEV_RTC_SHADOW_SECONDS 0xC
|
#define APBDEV_RTC_SHADOW_SECONDS 0xC
|
||||||
#define APBDEV_RTC_MILLI_SECONDS 0x10
|
#define APBDEV_RTC_MILLI_SECONDS 0x10
|
||||||
|
|
||||||
|
/*! SYSCTR0 registers. */
|
||||||
|
#define SYSCTR0_CNTFID0 0x20
|
||||||
|
|
||||||
/*! TMR registers. */
|
/*! TMR registers. */
|
||||||
#define TIMERUS_CNTR_1US (0x10 + 0x0)
|
#define TIMERUS_CNTR_1US (0x10 + 0x0)
|
||||||
#define TIMERUS_USEC_CFG (0x10 + 0x4)
|
#define TIMERUS_USEC_CFG (0x10 + 0x4)
|
||||||
|
Loading…
Reference in New Issue
Block a user