Compare commits

..

18 Commits
v1.3 ... v1.5

Author SHA1 Message Date
Kostas Missos
867977e416 Add v1.5 color changes and logo 2018-05-21 13:28:02 +03:00
Kostas Missos
5f39808923 Update to version 1.5 + add jsaowji's 3.0.0 support 2018-05-21 13:26:03 +03:00
Kostas Missos
b842342790 [Tools] Dump in 1GB parts if sd <=8GB + tui tweak
Tui tweak:
Support auto scroll.
2018-05-21 11:43:56 +03:00
Kostas Missos
5ec3efbff6 [Main] Remove redundant
And change some divisions into bitshifting
2018-05-21 11:35:55 +03:00
Kostas Missos
8cf3b62443 [Tools+Info] Utilize sd_save_to_file 2018-05-19 02:05:28 +03:00
Kostas Missos
7c90abcb06 [Main] Use printf macros to reduce clutter 2018-05-19 02:04:05 +03:00
Kostas Missos
5ae9f5655a [FatFS] Add error printing for important functions
So much needed
2018-05-19 02:01:39 +03:00
Kostas Missos
dcb77115c9 Whitespace/typo cleanup + others
Others:
*Add cluster size in SD card info
*Add error message for emmc read failure. Also fix return value.
*Added more comments and more constant naming
2018-05-17 15:42:01 -07:00
Kostas Missos
34981763a5 [Tools] Better dumping
*Remove option to ignore errors and abort right away.
 In the end what's the point if you end up with corrupted backup.
*Always create partial.idx before running on errors.
*Allow forced partial dumping on exFAT with large space, by creating the correct patial.idx file
*Some cleanup
2018-05-17 15:42:01 -07:00
Kostas Missos
645df34367 [Main] Unmount SD before reboot/power off 2018-05-17 15:42:01 -07:00
Kostas Missos
e06d833cf8 [sd] Correct ssr parsing + add write protect info 2018-05-15 12:40:04 -07:00
Kostas Missos
54187226c8 Better error messaging structure 2018-05-15 12:40:04 -07:00
Kostas Missos
8da015d993 Add missing include 2018-05-14 12:25:29 +12:00
Kostas Missos
0f5ffb4c43 Bugfixes
* Make button check delay 1s to avoid button repressing from "button ip" state
* Dumping: Fix part logic and honor user actions on ignoring errors
* Add time taken to dump emmc
2018-05-14 12:25:29 +12:00
Kostas Missos
8ddb6c16c5 [Tools] Add fuse/kfuse dumping to SD 2018-05-14 12:25:29 +12:00
Kostas Missos
82163a920e [Info] Add eMMC and SD info printing 2018-05-14 12:25:29 +12:00
Kostas Missos
55262fe9fa [sdmmc] Fixes Part 2
Now that the cid/csd/ssr/etc bytes/bitfields are correct:
* MMC/SD: Add cid/csd parsing and use new cid/csd variables
* SD: Add ssr (sd status) reading/parsing
2018-05-14 12:25:29 +12:00
Kostas Missos
acdc8b580c [sdmmc] Fixes Part 1
* MMC/SD: Change many hardcoded values to named ones
* MMC/SD: Fix scr and csd byte/bitfield ordering
* MMC: Add ext csd parsing and using these variables isntead of arrays
* MMC: Fix BKOPS support but disabled
* SD: Add partial sd v1 support
* SD: Fix we support low voltage OCR bit
* SD: Add scr parsing and using these variables instead of hardcoded ones
2018-05-14 12:25:29 +12:00
12 changed files with 2732 additions and 305 deletions

2258
ipl/ctc_logo.h Normal file

File diff suppressed because it is too large Load Diff

203
ipl/ff.c
View File

@@ -3,6 +3,7 @@
/-----------------------------------------------------------------------------/ /-----------------------------------------------------------------------------/
/ /
/ Copyright (C) 2017, ChaN, all right reserved. / Copyright (C) 2017, ChaN, all right reserved.
/ Copyright (c) 2018 naehrwert
/ /
/ FatFs module is an open source software. Redistribution and use of FatFs in / FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided / source and binary forms, with or without modification, are permitted provided
@@ -22,6 +23,11 @@
#include "ff.h" /* Declarations of FatFs API */ #include "ff.h" /* Declarations of FatFs API */
#include "diskio.h" /* Declarations of device I/O functions */ #include "diskio.h" /* Declarations of device I/O functions */
#include "gfx.h"
extern gfx_ctxt_t gfx_ctxt;
extern gfx_con_t gfx_con;
#define EFSPRINTF(text, ...) gfx_printf(&gfx_con, "\n\n%k[FatFS] "text"%k\n", 0xFF00FFFF, 0xFFFFFFFF)
//#define EFSPRINTF(...)
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
@@ -3253,6 +3259,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
stat = disk_status(fs->pdrv); stat = disk_status(fs->pdrv);
if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */
EFSPRINTF("Error: Write protected!");
return FR_WRITE_PROTECTED; return FR_WRITE_PROTECTED;
} }
return FR_OK; /* The filesystem object is valid */ return FR_OK; /* The filesystem object is valid */
@@ -3266,9 +3273,11 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */ fs->pdrv = LD2PD(vol); /* Bind the logical drive and a physical drive */
stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */ stat = disk_initialize(fs->pdrv); /* Initialize the physical drive */
if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ if (stat & STA_NOINIT) { /* Check if the initialization succeeded */
EFSPRINTF("Error: Medium not ready or hard error!");
return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */
} }
if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
EFSPRINTF("Error: Write protected!");
return FR_WRITE_PROTECTED; return FR_WRITE_PROTECTED;
} }
#if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */ #if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
@@ -3291,8 +3300,14 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */
} while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4); } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4);
} }
if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ if (fmt == 4) {
if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ EFSPRINTF("Error: Disk I/O error - Could not load boot record!");
return FR_DISK_ERR; /* An error occured in the disk I/O layer */
}
if (fmt >= 2) {
EFSPRINTF("Error: No FAT/FAT32/exFAT filesystem found!");
return FR_NO_FILESYSTEM; /* No FAT volume is found */
}
/* An FAT volume is found (bsect). Following code initializes the filesystem object */ /* An FAT volume is found (bsect). Following code initializes the filesystem object */
@@ -3303,36 +3318,58 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */ if (ld_word(fs->win + BPB_FSVerEx) != 0x100) {
EFSPRINTF("Error: exFAT - Version check failed!");
return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
}
if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
EFSPRINTF("Error: exFAT - Bytes per sector does not match physical sector size!");
return FR_NO_FILESYSTEM; return FR_NO_FILESYSTEM;
} }
maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */
if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ if (maxlba >= 0x100000000) {
EFSPRINTF("Error: exFAT - Cannot handle volume LBA with 32-bit LBA!");
return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */
}
fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ if (fs->n_fats != 1) {
EFSPRINTF("Error: exFAT - Multiple or no file allocation tables found!");
return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */
}
fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ if (fs->csize == 0) {
EFSPRINTF("Error: exFAT - Cluster size is not between 1KB - 32KB!");
return FR_NO_FILESYSTEM; /* (Must be 1..32768) */
}
nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */
if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ if (nclst > MAX_EXFAT) {
EFSPRINTF("Error: exFAT - Total clusters exceed allowed!");
return FR_NO_FILESYSTEM; /* (Too many clusters) */
}
fs->n_fatent = nclst + 2; fs->n_fatent = nclst + 2;
/* Boundaries and Limits */ /* Boundaries and Limits */
fs->volbase = bsect; fs->volbase = bsect;
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ if (maxlba < (QWORD)fs->database + nclst * fs->csize) {
EFSPRINTF("Error: exFAT - Volume size is lower than required!");
return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size required) */
}
fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
/* Check if bitmap location is in assumption (at the first cluster) */ /* Check if bitmap location is in assumption (at the first cluster) */
if (move_window(fs, clst2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; if (move_window(fs, clst2sect(fs, fs->dirbase)) != FR_OK) {
EFSPRINTF("Error: exFAT - Bitmap location not at first cluster!");
return FR_DISK_ERR;
}
for (i = 0; i < SS(fs); i += SZDIRE) { for (i = 0; i < SS(fs); i += SZDIRE) {
if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */
} }
@@ -3344,38 +3381,62 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
} else } else
#endif /* FF_FS_EXFAT */ #endif /* FF_FS_EXFAT */
{ {
if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) {
EFSPRINTF("Error: FAT - Bytes per sector does not match physical sector size!");
return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
}
fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
fs->fsize = fasize; fs->fsize = fasize;
fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ if (fs->n_fats != 1 && fs->n_fats != 2) {
EFSPRINTF("Error: FAT - No or more than 2 file allocation tables found!");
return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
}
fasize *= fs->n_fats; /* Number of sectors for FAT area */ fasize *= fs->n_fats; /* Number of sectors for FAT area */
fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) {
EFSPRINTF("Error: FAT - Cluster size is not a power of 2!");
return FR_NO_FILESYSTEM; /* (Must be power of 2) */
}
fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ if (fs->n_rootdir % (SS(fs) / SZDIRE)) {
EFSPRINTF("Error: FAT - Root directory entries are not sector aligned!");
return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
}
tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ if (nrsv == 0) {
EFSPRINTF("Error: FAT - Zero reserved sectors!");
return FR_NO_FILESYSTEM; /* (Must not be 0) */
}
/* Determine the FAT sub type */ /* Determine the FAT sub type */
sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */
if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ if (tsect < sysect) {
EFSPRINTF("Error: FAT - Invalid volume size!");
return FR_NO_FILESYSTEM; /* (Invalid volume size) */
}
nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ if (nclst == 0) {
EFSPRINTF("Error: FAT - Invalid volume size!");
return FR_NO_FILESYSTEM; /* (Invalid volume size) */
}
fmt = 0; fmt = 0;
if (nclst <= MAX_FAT32) fmt = FS_FAT32; if (nclst <= MAX_FAT32) fmt = FS_FAT32;
if (nclst <= MAX_FAT16) fmt = FS_FAT16; if (nclst <= MAX_FAT16) fmt = FS_FAT16;
if (nclst <= MAX_FAT12) fmt = FS_FAT12; if (nclst <= MAX_FAT12) fmt = FS_FAT12;
if (fmt == 0) return FR_NO_FILESYSTEM; if (fmt == 0) {
EFSPRINTF("Error: FAT - Not compatible FAT12/16/32 filesystem!");
return FR_NO_FILESYSTEM;
}
/* Boundaries and Limits */ /* Boundaries and Limits */
fs->n_fatent = nclst + 2; /* Number of FAT entries */ fs->n_fatent = nclst + 2; /* Number of FAT entries */
@@ -3383,17 +3444,29 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
fs->fatbase = bsect + nrsv; /* FAT start sector */ fs->fatbase = bsect + nrsv; /* FAT start sector */
fs->database = bsect + sysect; /* Data start sector */ fs->database = bsect + sysect; /* Data start sector */
if (fmt == FS_FAT32) { if (fmt == FS_FAT32) {
if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ if (ld_word(fs->win + BPB_FSVer32) != 0) {
if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ EFSPRINTF("Error: FAT32 - Not a 0.0 revision!");
return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
}
if (fs->n_rootdir != 0) {
EFSPRINTF("Error: FAT32 - Root entry sector is not 0!");
return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
}
fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */
szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
} else { } else {
if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ if (fs->n_rootdir == 0) {
EFSPRINTF("Error: FAT - Root entry sector is 0!");
return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
}
fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
} }
if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) {
EFSPRINTF("Error: FAT - FAT size is not the required size!");
return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
}
#if !FF_FS_READONLY #if !FF_FS_READONLY
/* Get FSInfo if available */ /* Get FSInfo if available */
@@ -3426,7 +3499,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
#if FF_USE_LFN == 1 #if FF_USE_LFN == 1
fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ fs->lfnbuf = LfnBuf; /* Static LFN working buffer */
#if FF_FS_EXFAT #if FF_FS_EXFAT
fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */ fs->dirbuf = DirBuf; /* Static directory block scratch-pad buffer */
#endif #endif
#endif #endif
#if FF_FS_RPATH != 0 #if FF_FS_RPATH != 0
@@ -3504,7 +3577,10 @@ FRESULT f_mount (
/* Get logical drive number */ /* Get logical drive number */
vol = get_ldnumber(&rp); vol = get_ldnumber(&rp);
if (vol < 0) return FR_INVALID_DRIVE; if (vol < 0) {
EFSPRINTF("Error: Invalid drive!");
return FR_INVALID_DRIVE;
}
cfs = FatFs[vol]; /* Pointer to fs object */ cfs = FatFs[vol]; /* Pointer to fs object */
if (cfs) { if (cfs) {
@@ -3745,8 +3821,14 @@ FRESULT f_read (
*br = 0; /* Clear read byte counter */ *br = 0; /* Clear read byte counter */
res = validate(&fp->obj, &fs); /* Check validity of the file object */ res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ EFSPRINTF("Error: File object Validation!");
LEAVE_FF(fs, res); /* Check validity */
}
if (!(fp->flag & FA_READ)) {
EFSPRINTF("Error: Access denied!");
LEAVE_FF(fs, FR_DENIED); /* Check access mode */
}
remain = fp->obj.objsize - fp->fptr; remain = fp->obj.objsize - fp->fptr;
if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
@@ -3767,19 +3849,31 @@ FRESULT f_read (
clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */
} }
} }
if (clst < 2) ABORT(fs, FR_INT_ERR); if (clst < 2) {
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); EFSPRINTF("Error: Cluster status check or Internal error!");
ABORT(fs, FR_INT_ERR);
}
if (clst == 0xFFFFFFFF) {
EFSPRINTF("Error: Disk error (cluster hard error)!");
ABORT(fs, FR_DISK_ERR);
}
fp->clust = clst; /* Update current cluster */ fp->clust = clst; /* Update current cluster */
} }
sect = clst2sect(fs, fp->clust); /* Get current sector */ sect = clst2sect(fs, fp->clust); /* Get current sector */
if (sect == 0) ABORT(fs, FR_INT_ERR); if (sect == 0) {
EFSPRINTF("Error: Get current sector error!");
ABORT(fs, FR_INT_ERR);
}
sect += csect; sect += csect;
cc = btr / SS(fs); /* When remaining bytes >= sector size, */ cc = btr / SS(fs); /* When remaining bytes >= sector size, */
if (cc > 0) { /* Read maximum contiguous sectors directly */ if (cc > 0) { /* Read maximum contiguous sectors directly */
if (csect + cc > fs->csize) { /* Clip at cluster boundary */ if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect; cc = fs->csize - csect;
} }
if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) {
EFSPRINTF("Error: Read - Low level disk I/O!");
ABORT(fs, FR_DISK_ERR);
}
#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ #if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
#if FF_FS_TINY #if FF_FS_TINY
if (fs->wflag && fs->winsect - sect < cc) { if (fs->wflag && fs->winsect - sect < cc) {
@@ -3798,11 +3892,17 @@ FRESULT f_read (
if (fp->sect != sect) { /* Load data sector if not in cache */ if (fp->sect != sect) { /* Load data sector if not in cache */
#if !FF_FS_READONLY #if !FF_FS_READONLY
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
EFSPRINTF("Error: Write-back dirty sector cache!");
ABORT(fs, FR_DISK_ERR);
}
fp->flag &= (BYTE)~FA_DIRTY; fp->flag &= (BYTE)~FA_DIRTY;
} }
#endif #endif
if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
EFSPRINTF("Error: Read - Low level disk I/O!\n(fill sector cache)");
ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
}
} }
#endif #endif
fp->sect = sect; fp->sect = sect;
@@ -3844,8 +3944,14 @@ FRESULT f_write (
*bw = 0; /* Clear write byte counter */ *bw = 0; /* Clear write byte counter */
res = validate(&fp->obj, &fs); /* Check validity of the file object */ res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ EFSPRINTF("Error: File object Validation!");
LEAVE_FF(fs, res); /* Check validity */
}
if (!(fp->flag & FA_WRITE)) {
EFSPRINTF("Error: Access denied!");
LEAVE_FF(fs, FR_DENIED); /* Check access mode */
}
/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */ /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
@@ -3872,9 +3978,18 @@ FRESULT f_write (
clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
} }
} }
if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ if (clst == 0) {
if (clst == 1) ABORT(fs, FR_INT_ERR); EFSPRINTF("Error: Could not allocate a new cluster\n(disk full or low level disk I/O error)!");
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); break; /* Could not allocate a new cluster (disk full) */
}
if (clst == 1) {
EFSPRINTF("Error: Cluster status check or Internal error!");
ABORT(fs, FR_INT_ERR);
}
if (clst == 0xFFFFFFFF) {
EFSPRINTF("Error: Disk error (cluster hard error)!");
ABORT(fs, FR_DISK_ERR);
}
fp->clust = clst; /* Update current cluster */ fp->clust = clst; /* Update current cluster */
if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
} }
@@ -3882,19 +3997,28 @@ FRESULT f_write (
if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */
#else #else
if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
EFSPRINTF("Error: Write-back sector cache!");
ABORT(fs, FR_DISK_ERR);
}
fp->flag &= (BYTE)~FA_DIRTY; fp->flag &= (BYTE)~FA_DIRTY;
} }
#endif #endif
sect = clst2sect(fs, fp->clust); /* Get current sector */ sect = clst2sect(fs, fp->clust); /* Get current sector */
if (sect == 0) ABORT(fs, FR_INT_ERR); if (sect == 0) {
EFSPRINTF("Error: Get current sector error!");
ABORT(fs, FR_INT_ERR);
}
sect += csect; sect += csect;
cc = btw / SS(fs); /* When remaining bytes >= sector size, */ cc = btw / SS(fs); /* When remaining bytes >= sector size, */
if (cc > 0) { /* Write maximum contiguous sectors directly */ if (cc > 0) { /* Write maximum contiguous sectors directly */
if (csect + cc > fs->csize) { /* Clip at cluster boundary */ if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect; cc = fs->csize - csect;
} }
if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) {
EFSPRINTF("Error: Write - Low level disk I/O!");
ABORT(fs, FR_DISK_ERR);
}
#if FF_FS_MINIMIZE <= 2 #if FF_FS_MINIMIZE <= 2
#if FF_FS_TINY #if FF_FS_TINY
if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
@@ -3920,6 +4044,7 @@ FRESULT f_write (
if (fp->sect != sect && /* Fill sector cache with file data */ if (fp->sect != sect && /* Fill sector cache with file data */
fp->fptr < fp->obj.objsize && fp->fptr < fp->obj.objsize &&
disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) { disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
EFSPRINTF("Error: Read - Low level disk I/O!\n(Could not fill sector cache with file data)");
ABORT(fs, FR_DISK_ERR); ABORT(fs, FR_DISK_ERR);
} }
#endif #endif

View File

@@ -89,7 +89,7 @@ void gfx_con_init(gfx_con_t *con, gfx_ctxt_t *ctxt)
con->y = 0; con->y = 0;
con->fgcol = 0xFFFFFFFF; con->fgcol = 0xFFFFFFFF;
con->fillbg = 0; con->fillbg = 0;
con->bgcol = 0xFF000000; con->bgcol = 0xFF1B1B1B;
} }
void gfx_con_setcol(gfx_con_t *con, u32 fgcol, int fillbg, u32 bgcol) void gfx_con_setcol(gfx_con_t *con, u32 fgcol, int fillbg, u32 bgcol)
@@ -307,3 +307,17 @@ void gfx_line(gfx_ctxt_t *ctxt, int x0, int y0, int x1, int y1, u32 color)
if (e2 < dy) { err += dx; y0 += sy; } if (e2 < dy) { err += dx; y0 += sy; }
} }
} }
void gfx_set_logo(gfx_ctxt_t *ctxt, u16 pos_x, u16 pos_y,
const u16 size_x, const u16 size_y, const u8 *buf)
{
u32 pos = 0;
for (u32 y = pos_y; y < pos_y + size_y; y++)
{
for (u32 x = pos_x; x < pos_x + size_x; x++)
{
ctxt->fb[x + y*ctxt->stride] = (0xFF << 24) | buf[pos] | (buf[pos + 1] << 8) | (buf[pos + 2] << 16);
pos+=3;
}
}
}

View File

@@ -50,5 +50,6 @@ void gfx_hexdump(gfx_con_t *con, u32 base, const u8 *buf, u32 len);
void gfx_set_pixel(gfx_ctxt_t *ctxt, u32 x, u32 y, u32 color); void gfx_set_pixel(gfx_ctxt_t *ctxt, u32 x, u32 y, u32 color);
void gfx_line(gfx_ctxt_t *ctxt, int x0, int y0, int x1, int y1, u32 color); void gfx_line(gfx_ctxt_t *ctxt, int x0, int y0, int x1, int y1, u32 color);
void gfx_set_logo(gfx_ctxt_t *ctxt, u16 pos_x, u16 pos_y, const u16 size_x, const u16 size_y, const u8 *buf);
#endif #endif

View File

@@ -433,12 +433,21 @@ DPRINTF("decrypted and unpacked pkg1\n");
se_aes_key_clear(8); se_aes_key_clear(8);
se_aes_key_clear(11); se_aes_key_clear(11);
//se_aes_key_clear(13);
//se_key_acc_ctrl(10, 0xFF); switch (ctxt.pkg1_id->kb) {
case KB_FIRMWARE_VERSION_100_200:
case KB_FIRMWARE_VERSION_300:
case KB_FIRMWARE_VERSION_301:
se_key_acc_ctrl(12, 0xFF);
se_key_acc_ctrl(13, 0xFF);
break;
default:
case KB_FIRMWARE_VERSION_400:
case KB_FIRMWARE_VERSION_500:
se_key_acc_ctrl(12, 0xFF); se_key_acc_ctrl(12, 0xFF);
//se_key_acc_ctrl(13, 0xFF);
//se_key_acc_ctrl(14, 0xFF);
se_key_acc_ctrl(15, 0xFF); se_key_acc_ctrl(15, 0xFF);
break;
}
//Clear 'BootConfig'. //Clear 'BootConfig'.
memset((void *)0x4003D000, 0, 0x3000); memset((void *)0x4003D000, 0, 0x3000);
@@ -472,8 +481,6 @@ DPRINTF("decrypted and unpacked pkg1\n");
*mb_in = 3; *mb_in = 3;
sleep(100); sleep(100);
/*PMC(0x4) = 0x7FFFF3; /*PMC(0x4) = 0x7FFFF3;
PMC(0x2C4) = 0xFFFFFFFF; PMC(0x2C4) = 0xFFFFFFFF;
PMC(0x2D8) = 0xFFAFFFFF; PMC(0x2D8) = 0xFFAFFFFF;

View File

@@ -48,6 +48,14 @@
#include "pkg1.h" #include "pkg1.h"
#include "mmc.h" #include "mmc.h"
//TODO: ugly.
gfx_ctxt_t gfx_ctxt;
gfx_con_t gfx_con;
//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", 0xFF0000FF, 0xFFFFFFFF)
#define EPRINTFARGS(text, args...) gfx_printf(&gfx_con, "%k"text"%k\n", 0xFF0000FF, args, 0xFFFFFFFF)
//TODO: ugly. //TODO: ugly.
sdmmc_t sd_sdmmc; sdmmc_t sd_sdmmc;
sdmmc_storage_t sd_storage; sdmmc_storage_t sd_storage;
@@ -59,16 +67,38 @@ int sd_mount()
if (sd_mounted) if (sd_mounted)
return 1; return 1;
if (sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11) && if (!sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11))
f_mount(&sd_fs, "", 1) == FR_OK) {
EPRINTF("Failed to init SD card (make sure that it is inserted).");
}
else
{
int res = 0;
res = f_mount(&sd_fs, "", 1);
if (res == FR_OK)
{ {
sd_mounted = 1; sd_mounted = 1;
return 1; return 1;
} }
else
{
EPRINTFARGS("Failed to mount SD card (FatFS Error %d).\n(make sure that a FAT32/exFAT partition exists)", res);
}
}
return 0; return 0;
} }
void sd_unmount()
{
if (sd_mounted)
{
gfx_puts(&gfx_con, "Unmounting SD card...\n");
f_mount(NULL, "", 1);
sdmmc_storage_end(&sd_storage);
}
}
void *sd_file_read(char *path) void *sd_file_read(char *path)
{ {
FIL fp; FIL fp;
@@ -97,6 +127,20 @@ void *sd_file_read(char *path)
return buf; return buf;
} }
int sd_save_to_file(void * buf, u32 size, const char * filename)
{
FIL fp;
if (f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
return 1;
}
f_sync(&fp);
f_write(&fp, buf, size, NULL);
f_close(&fp);
return 0;
}
void panic(u32 val) void panic(u32 val)
{ {
//Set panic code. //Set panic code.
@@ -229,7 +273,7 @@ void config_se_brom()
void config_hw() void config_hw()
{ {
//Bootrom stuff we skipped by going thru rcm. //Bootrom stuff we skipped by going through rcm.
config_se_brom(); config_se_brom();
//FUSE(FUSE_PRIVATEKEYDISABLE) = 0x11; //FUSE(FUSE_PRIVATEKEYDISABLE) = 0x11;
SYSREG(0x110) &= 0xFFFFFF9F; SYSREG(0x110) &= 0xFFFFFF9F;
@@ -290,13 +334,9 @@ void config_hw()
sdram_lp0_save_params(sdram_get_params()); sdram_lp0_save_params(sdram_get_params());
} }
//TODO: ugly.
gfx_ctxt_t gfx_ctxt;
gfx_con_t gfx_con;
void print_fuseinfo() void print_fuseinfo()
{ {
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
gfx_printf(&gfx_con, "%k(Unlocked) fuse cache:\n\n%k", 0xFFFF9955, 0xFFFFFFFF); gfx_printf(&gfx_con, "%k(Unlocked) fuse cache:\n\n%k", 0xFFFF9955, 0xFFFFFFFF);
@@ -309,25 +349,17 @@ void print_fuseinfo()
u32 btn = btn_wait(); u32 btn = btn_wait();
if (btn & BTN_POWER) if (btn & BTN_POWER)
{ {
if (!sd_mount()) if (sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n",
0xFF0000FF, 0xFFFFFFFF);
}
else
{ {
FIL fuseFp; FIL fuseFp;
char fuseFilename[9]; char fuseFilename[9];
memcpy(fuseFilename, "fuse.bin", 8); memcpy(fuseFilename, "fuse.bin", 8);
fuseFilename[8] = 0; fuseFilename[8] = 0;
if (f_open(&fuseFp, fuseFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
{ if (sd_save_to_file((u8 *)0x7000F900, 0x2FC, fuseFilename))
f_write(&fuseFp, (u8 *)0x7000F900, 0x2FC, NULL); EPRINTF("\nError creating fuse.bin file.");
f_close(&fuseFp);
gfx_puts(&gfx_con, "\nDone!\n");
}
else else
gfx_printf(&gfx_con, "%k\nError creating fuse.bin file.%k\n", 0xFF0000FF, 0xFFFFFFFF); gfx_puts(&gfx_con, "\nDone!\n");
} }
sleep(2000000); sleep(2000000);
btn_wait(); btn_wait();
@@ -336,13 +368,13 @@ void print_fuseinfo()
void print_kfuseinfo() void print_kfuseinfo()
{ {
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
gfx_printf(&gfx_con, "%kKFuse contents:\n\n%k", 0xFFFF9955, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kKFuse contents:\n\n%k", 0xFFFF9955, 0xFFFFFFFF);
u32 buf[KFUSE_NUM_WORDS]; u32 buf[KFUSE_NUM_WORDS];
if (!kfuse_read(buf)) if (!kfuse_read(buf))
gfx_printf(&gfx_con, "%kCRC fail.\n", 0xFF0000FF); EPRINTF("CRC fail.");
else else
gfx_hexdump(&gfx_con, 0, (u8 *)buf, KFUSE_NUM_WORDS * 4); gfx_hexdump(&gfx_con, 0, (u8 *)buf, KFUSE_NUM_WORDS * 4);
@@ -353,25 +385,17 @@ void print_kfuseinfo()
u32 btn = btn_wait(); u32 btn = btn_wait();
if (btn & BTN_POWER) if (btn & BTN_POWER)
{ {
if (!sd_mount()) if (sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n",
0xFF0000FF, 0xFFFFFFFF);
}
else
{ {
FIL kfuseFp; FIL kfuseFp;
char kfuseFilename[10]; char kfuseFilename[10];
memcpy(kfuseFilename, "kfuse.bin", 9); memcpy(kfuseFilename, "kfuse.bin", 9);
kfuseFilename[9] = 0; kfuseFilename[9] = 0;
if (f_open(&kfuseFp, kfuseFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
{ if (sd_save_to_file((u8 *)buf, KFUSE_NUM_WORDS * 4, kfuseFilename))
f_write(&kfuseFp, (u8 *)buf, KFUSE_NUM_WORDS * 4, NULL); EPRINTF("\nError creating kfuse.bin file.");
f_close(&kfuseFp);
gfx_puts(&gfx_con, "\nDone!\n");
}
else else
gfx_printf(&gfx_con, "%k\nError creating kfuse.bin file.%k\n", 0xFF0000FF, 0xFFFFFFFF); gfx_puts(&gfx_con, "\nDone!\n");
} }
sleep(2000000); sleep(2000000);
btn_wait(); btn_wait();
@@ -380,17 +404,17 @@ void print_kfuseinfo()
void print_mmc_info() void print_mmc_info()
{ {
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
static const u32 SECTORS_TO_MIB_COEFF = 0x800; static const u32 SECTORS_TO_MIB_COEFF = 11;
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; sdmmc_t sdmmc;
if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4))
{ {
gfx_printf(&gfx_con, "%kFailed to init eMMC.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Failed to init eMMC.");
goto out; goto out;
} }
else else
@@ -433,12 +457,12 @@ void print_mmc_info()
storage.cid.prv, storage.cid.serial, storage.cid.month, storage.cid.year); storage.cid.prv, storage.cid.serial, storage.cid.month, storage.cid.year);
break; break;
default: default:
gfx_printf(&gfx_con, "%keMMC has unknown MMCA version %d%k\n", 0xFF0000FF, storage.csd.mmca_vsn, 0xFFFFFFFF); EPRINTFARGS("eMMC has unknown MMCA version %d", storage.csd.mmca_vsn);
break; break;
} }
if (storage.csd.structure == 0) if (storage.csd.structure == 0)
gfx_printf(&gfx_con, "%kUnknown CSD structure%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Unknown CSD structure.");
else else
{ {
gfx_printf(&gfx_con, "%kExtended Card-Specific Data V1.%d:%k\n", gfx_printf(&gfx_con, "%kExtended Card-Specific Data V1.%d:%k\n",
@@ -499,7 +523,7 @@ void print_mmc_info()
gfx_printf(&gfx_con, " 3: %kRPMB %kSize: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF00FF96, 0xFFFFFFFF, gfx_printf(&gfx_con, " 3: %kRPMB %kSize: %5d KiB (LBA Sectors: 0x%07X)\n", 0xFF00FF96, 0xFFFFFFFF,
rpmb_size / 1024, rpmb_size / 1024 / 512); rpmb_size / 1024, rpmb_size / 1024 / 512);
gfx_printf(&gfx_con, " 0: %kGPP (USER) %kSize: %05d MiB (LBA Sectors: 0x%07X)\n\n", 0xFF00FF96, 0xFFFFFFFF, gfx_printf(&gfx_con, " 0: %kGPP (USER) %kSize: %05d MiB (LBA Sectors: 0x%07X)\n\n", 0xFF00FF96, 0xFFFFFFFF,
storage.sec_cnt / SECTORS_TO_MIB_COEFF, storage.sec_cnt); storage.sec_cnt >> SECTORS_TO_MIB_COEFF, storage.sec_cnt);
gfx_printf(&gfx_con, "%kGPP (eMMC USER) partition table:%k\n", 0xFFFFDD00, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kGPP (eMMC USER) partition table:%k\n", 0xFFFFDD00, 0xFFFFFFFF);
sdmmc_storage_set_mmc_partition(&storage, 0); sdmmc_storage_set_mmc_partition(&storage, 0);
@@ -509,7 +533,7 @@ void print_mmc_info()
LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link) 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, LBA Range: %08X-%08X)%k\n", gfx_printf(&gfx_con, " %02d: %k%s%k\n Size: % 5d MiB (LBA Sectors 0x%07X, LBA Range: %08X-%08X)%k\n",
gpp_idx++, 0xFF14FDAE, part->name, 0xFFFFFFFF, (part->lba_end - part->lba_start + 1) / SECTORS_TO_MIB_COEFF, gpp_idx++, 0xFF14FDAE, part->name, 0xFFFFFFFF, (part->lba_end - part->lba_start + 1) >> SECTORS_TO_MIB_COEFF,
part->lba_end - part->lba_start + 1, part->lba_start, part->lba_end, 0xFFFFFFFF); part->lba_end - part->lba_start + 1, part->lba_start, part->lba_end, 0xFFFFFFFF);
} }
} }
@@ -523,17 +547,12 @@ out:
void print_sdcard_info() void print_sdcard_info()
{ {
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
static const u32 SECTORS_TO_MIB_COEFF = 0x800; static const u32 SECTORS_TO_MIB_COEFF = 11;
if (!sd_mount()) if (sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n",
0xFF0000FF, 0xFFFFFFFF);
}
else
{ {
u32 capacity; u32 capacity;
@@ -553,37 +572,25 @@ void print_sdcard_info()
sd_storage.cid.month, sd_storage.cid.year); sd_storage.cid.month, sd_storage.cid.year);
gfx_printf(&gfx_con, "%kCard-Specific Data V%d.0:%k\n", 0xFFFFDD00, sd_storage.csd.structure + 1, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kCard-Specific Data V%d.0:%k\n", 0xFFFFDD00, sd_storage.csd.structure + 1, 0xFFFFFFFF);
switch(sd_storage.csd.structure) capacity = sd_storage.csd.capacity >> (20 - sd_storage.csd.read_blkbits);
{
case 0:
capacity = (sd_storage.csd.capacity << sd_storage.csd.read_blkbits) / 1024 / 1024;
gfx_printf(&gfx_con, gfx_printf(&gfx_con,
" Cmd Classes: %02X\n\ " Cmd Classes: %02X\n\
Capacity: %d MiB\n", Capacity: %d MiB\n\
sd_storage.csd.cmdclass, capacity); Bus Width: %d\n\
break;
case 1:
capacity = (sd_storage.csd.c_size << sd_storage.csd.read_blkbits) / 1024;
gfx_printf(&gfx_con,
" Cmd Classes: %02X\n\
Capacity: %d MiB\n",
sd_storage.csd.cmdclass, capacity);
break;
}
gfx_printf(&gfx_con,
" Bus Width: %d\n\
Speed Class: %d\n\ Speed Class: %d\n\
UHS Grade: U%d\n\ UHS Grade: U%d\n\
Video Class: V%d\n\ Video Class: V%d\n\
App perf class: A%d\n\n", App perf class: A%d\n\
Write Protect: %d\n\n",
sd_storage.csd.cmdclass, capacity,
sd_storage.ssr.bus_width, sd_storage.ssr.speed_class, sd_storage.ssr.uhs_grade, sd_storage.ssr.bus_width, sd_storage.ssr.speed_class, sd_storage.ssr.uhs_grade,
sd_storage.ssr.video_class, sd_storage.ssr.app_class); 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"); gfx_puts(&gfx_con, "Acquiring FAT volume info...\n\n");
f_getfree("", &sd_fs.free_clst, NULL); f_getfree("", &sd_fs.free_clst, NULL);
gfx_printf(&gfx_con, "%kFound %s volume:%k\n Free: %d MiB\n", 0xFFFFDD00, gfx_printf(&gfx_con, "%kFound %s volume:%k\n Free: %d MiB\n Cluster: %d B\n",
sd_fs.fs_type == FS_EXFAT ? "exFAT" : "FAT32", 0xFFFFFFFF, 0xFFFFDD00, sd_fs.fs_type == FS_EXFAT ? "exFAT" : "FAT32", 0xFFFFFFFF,
sd_fs.free_clst * sd_fs.csize / SECTORS_TO_MIB_COEFF); sd_fs.free_clst * sd_fs.csize >> SECTORS_TO_MIB_COEFF, sd_fs.csize * 512);
} }
sleep(1000000); sleep(1000000);
@@ -593,7 +600,7 @@ void print_sdcard_info()
void print_tsec_key() void print_tsec_key()
{ {
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
sdmmc_storage_t storage; sdmmc_storage_t storage;
@@ -608,8 +615,8 @@ void print_tsec_key()
const pkg1_id_t *pkg1_id = pkg1_identify(pkg1); const pkg1_id_t *pkg1_id = pkg1_identify(pkg1);
if (!pkg1_id) if (!pkg1_id)
{ {
gfx_printf(&gfx_con, "%kCould not identify package 1 version to read TSEC firmware (= '%s').%k\n", EPRINTFARGS("Could not identify package1 version to read TSEC firmware (= '%s').",
0xFF0000FF, (char *)pkg1 + 0x10, 0xFFFFFFFF); (char *)pkg1 + 0x10);
goto out; goto out;
} }
@@ -625,7 +632,7 @@ void print_tsec_key()
gfx_printf(&gfx_con, "%02X", key[i]); gfx_printf(&gfx_con, "%02X", key[i]);
} }
else else
gfx_printf(&gfx_con, "%kERROR %X", 0xFF0000FF, res); EPRINTFARGS("ERROR %X", res);
gfx_putc(&gfx_con, '\n'); gfx_putc(&gfx_con, '\n');
} }
@@ -638,11 +645,13 @@ out:;
void reboot_normal() void reboot_normal()
{ {
sd_unmount();
panic(0x21); //Bypass fuse programming in package1. panic(0x21); //Bypass fuse programming in package1.
} }
void reboot_rcm() void reboot_rcm()
{ {
sd_unmount();
PMC(APBDEV_PMC_SCRATCH0) = 2; //Reboot into rcm. PMC(APBDEV_PMC_SCRATCH0) = 2; //Reboot into rcm.
PMC(0) |= 0x10; PMC(0) |= 0x10;
while (1) while (1)
@@ -651,6 +660,7 @@ void reboot_rcm()
void power_off() void power_off()
{ {
sd_unmount();
//TODO: we should probably make sure all regulators are powered off properly. //TODO: we should probably make sure all regulators are powered off properly.
i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_PWR_OFF); i2c_send_byte(I2C_5, 0x3C, MAX77620_REG_ONOFFCNFG1, MAX77620_ONOFFCNFG1_PWR_OFF);
} }
@@ -658,9 +668,9 @@ void power_off()
int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part) 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 FAT32_FILESIZE_LIMIT = 0xFFFFFFFF;
static const u32 MULTIPART_SPLIT_SIZE = (1u << 31); static const u32 SECTORS_TO_MIB_COEFF = 11;
static const u32 SECTORS_TO_MIB_COEFF = 0x800;
u32 multipartSplitSize = (1u << 31);
u32 totalSectors = part->lba_end - part->lba_start + 1; u32 totalSectors = part->lba_end - part->lba_start + 1;
u32 currPartIdx = 0; u32 currPartIdx = 0;
u32 numSplitParts = 0; u32 numSplitParts = 0;
@@ -668,7 +678,6 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
int isSmallSdCard = 0; int isSmallSdCard = 0;
int partialDumpInProgress = 0; int partialDumpInProgress = 0;
int res = 0; int res = 0;
int ignoreWriteErrors = 0;
char* outFilename = sd_path; char* outFilename = sd_path;
u32 sdPathLen = strlen(sd_path); u32 sdPathLen = strlen(sd_path);
@@ -678,49 +687,59 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
partialIdxFilename[11] = 0; partialIdxFilename[11] = 0;
gfx_printf(&gfx_con, "SD Card free space: %d MiB, Total dump size %d MiB\n", gfx_printf(&gfx_con, "SD Card free space: %d MiB, Total dump size %d MiB\n",
sd_fs.free_clst * sd_fs.csize / SECTORS_TO_MIB_COEFF, sd_fs.free_clst * sd_fs.csize >> SECTORS_TO_MIB_COEFF,
totalSectors / 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 // Check if the USER partition or the RAW eMMC fits the sd card free space
if (totalSectors > (sd_fs.free_clst * sd_fs.csize)) if (totalSectors > (sd_fs.free_clst * sd_fs.csize))
{ {
isSmallSdCard = 1; isSmallSdCard = 1;
gfx_printf(&gfx_con, "%kSD card free space is smaller than dump total size.%k\n", 0xFF00BAFF, 0xFFFFFFFF); gfx_printf(&gfx_con, "%k\nSD card free space is smaller than dump total size.%k\n", 0xFF00BAFF, 0xFFFFFFFF);
maxSplitParts = (sd_fs.free_clst * sd_fs.csize) / (MULTIPART_SPLIT_SIZE / 512);
if (!maxSplitParts) if (!maxSplitParts)
{ {
gfx_printf(&gfx_con, "%kNot enough free space for partial dumping.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Not enough free space for partial dumping.");
return 0; return 0;
} }
} }
// Check if we continueing a previous raw eMMC dump in progress. // Check if we continuing a previous raw eMMC dump in progress.
if (isSmallSdCard)
{
if (f_open(&partialIdxFp, partialIdxFilename, FA_READ) == FR_OK) if (f_open(&partialIdxFp, partialIdxFilename, FA_READ) == FR_OK)
{ {
gfx_printf(&gfx_con, "%kFound partial dump in progress. Continuing...%k\n", 0xFF14FDAE, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kFound partial dump in progress. Continuing...%k\n\n", 0xFF14FDAE, 0xFFFFFFFF);
partialDumpInProgress = 1; partialDumpInProgress = 1;
// Force partial dumping, even if the card is larger.
isSmallSdCard = 1;
f_read(&partialIdxFp, &currPartIdx, 4, NULL); f_read(&partialIdxFp, &currPartIdx, 4, NULL);
f_close(&partialIdxFp); f_close(&partialIdxFp);
if (!maxSplitParts)
{
EPRINTF("Not enough free space for partial dumping.");
return 0;
}
// Increase maxSplitParts to accommodate previously dumped parts // Increase maxSplitParts to accommodate previously dumped parts
maxSplitParts += currPartIdx; maxSplitParts += currPartIdx;
} }
else else
gfx_printf(&gfx_con, "%kContinuing with partial dumping...%k\n", 0xFF00BAFF, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kPartial dumping enabled (with %d MiB parts)...%k\n\n", 0xFF00BAFF, multipartSplitSize >> 20, 0xFFFFFFFF);
}
// Check if filesystem is FAT32 or the free space is smaller and dump in parts // Check if filesystem is FAT32 or the free space is smaller and dump in parts
if (((sd_fs.fs_type != FS_EXFAT) && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) | isSmallSdCard) if (((sd_fs.fs_type != FS_EXFAT) && totalSectors > (FAT32_FILESIZE_LIMIT / NX_EMMC_BLOCKSIZE)) | isSmallSdCard)
{ {
static const u32 MULTIPART_SPLIT_SECTORS = MULTIPART_SPLIT_SIZE/NX_EMMC_BLOCKSIZE; u32 multipartSplitSectors = multipartSplitSize / NX_EMMC_BLOCKSIZE;
numSplitParts = (totalSectors+MULTIPART_SPLIT_SECTORS-1)/MULTIPART_SPLIT_SECTORS; numSplitParts = (totalSectors + multipartSplitSectors - 1) / multipartSplitSectors;
outFilename = alloca(sdPathLen+4); outFilename = alloca(sdPathLen+4);
memcpy(outFilename, sd_path, sdPathLen); memcpy(outFilename, sd_path, sdPathLen);
@@ -737,7 +756,7 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
else else
outFilename[sdPathLen + 1] = 0; outFilename[sdPathLen + 1] = 0;
} }
// Continue from where we left, if partial dump in proggress. // Continue from where we left, if partial dump in progress.
else else
{ {
if (numSplitParts >= 10 && currPartIdx < 10) if (numSplitParts >= 10 && currPartIdx < 10)
@@ -752,7 +771,11 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
FIL fp; FIL fp;
if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
{
EPRINTFARGS("Error creating file %s.", outFilename);
return 0; return 0;
}
static const u32 NUM_SECTORS_PER_ITER = 512; static const u32 NUM_SECTORS_PER_ITER = 512;
u8 *buf = (u8 *)malloc(NX_EMMC_BLOCKSIZE * NUM_SECTORS_PER_ITER); u8 *buf = (u8 *)malloc(NX_EMMC_BLOCKSIZE * NUM_SECTORS_PER_ITER);
@@ -762,16 +785,16 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
u32 prevPct = 200; u32 prevPct = 200;
int retryCount = 0; int retryCount = 0;
// Continue from where we left, if partial dump in proggress. // Continue from where we left, if partial dump in progress.
if (partialDumpInProgress) if (partialDumpInProgress)
{ {
lba_curr += currPartIdx * (MULTIPART_SPLIT_SIZE / NX_EMMC_BLOCKSIZE); lba_curr += currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE);
totalSectors -= currPartIdx * (MULTIPART_SPLIT_SIZE / NX_EMMC_BLOCKSIZE); totalSectors -= currPartIdx * (multipartSplitSize / NX_EMMC_BLOCKSIZE);
} }
while(totalSectors > 0) while(totalSectors > 0)
{ {
if (numSplitParts != 0 && bytesWritten >= MULTIPART_SPLIT_SIZE) if (numSplitParts != 0 && bytesWritten >= multipartSplitSize)
{ {
f_close(&fp); f_close(&fp);
memset(&fp, 0, sizeof(fp)); memset(&fp, 0, sizeof(fp));
@@ -785,8 +808,8 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
else else
itoa(currPartIdx, &outFilename[sdPathLen], 10); itoa(currPartIdx, &outFilename[sdPathLen], 10);
// More parts to dump that do not currently fit the sd card free space // Always create partial.idx before next part, in case a fatal error occurs.
if ((isSmallSdCard && currPartIdx >= maxSplitParts) || (res && !ignoreWriteErrors)) if (isSmallSdCard)
{ {
// Create partial dump index file // Create partial dump index file
if (f_open(&partialIdxFp, partialIdxFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) if (f_open(&partialIdxFp, partialIdxFilename, FA_CREATE_ALWAYS | FA_WRITE) == FR_OK)
@@ -796,32 +819,31 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
} }
else else
{ {
gfx_printf(&gfx_con, "%k\nError creating partial.idx file.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("\nError creating partial.idx file.");
free(buf); free(buf);
return 0; return 0;
} }
// Sd card fatal error.
if (res && !ignoreWriteErrors) // More parts to dump that do not currently fit the sd card free space or fatal error
if (currPartIdx >= maxSplitParts)
{ {
gfx_printf(&gfx_con, "%k\nPress any key and try again.%k\n", gfx_puts(&gfx_con, "\n\n1. Press any key and Power off Switch from the main menu.\n\
0xFF0000FF, 0xFFFFFFFF);
free(buf);
return 0;
}
gfx_puts(&gfx_con, "\n1. Press any key and Power off Switch from the main menu.\n\
2. Move the files from SD card to free space.\n\ 2. Move the files from SD card to free space.\n\
Don\'t move the partial.idx file!\n\ Don\'t move the partial.idx file!\n\
3. Unplug and re-plug USB while pressing Vol+.\n\ 3. Unplug and re-plug USB while pressing Vol+.\n\
4. Run hekate - ipl again and press Dump RAW eMMC or eMMC USER to continue"); 4. Run hekate - ipl again and press Dump RAW eMMC or eMMC USER to continue\n");
free(buf); free(buf);
return 1; return 1;
} }
}
// Create next part
if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
{ {
EPRINTFARGS("Error creating file %s.", outFilename);
free(buf); free(buf);
return 0; return 0;
} }
@@ -832,30 +854,29 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
u32 num = MIN(totalSectors, NUM_SECTORS_PER_ITER); u32 num = MIN(totalSectors, NUM_SECTORS_PER_ITER);
while(!sdmmc_storage_read(storage, lba_curr, num, buf)) while(!sdmmc_storage_read(storage, lba_curr, num, buf))
{ {
gfx_printf(&gfx_con, "%kError reading %d blocks @ LBA %08X from eMMC (try %d)%k\n", EPRINTFARGS("Error reading %d blocks @ LBA %08X from eMMC (try %d)",
0xFF0000FF, num, lba_curr, ++retryCount, 0xFFFFFFFF); num, lba_curr, ++retryCount);
sleep(500000); sleep(500000);
if (retryCount >= 10) if (retryCount >= 10)
goto out; {
EPRINTFARGS("\nFailed to read %d blocks @ LBA %08X from eMMC. Aborting..",
num, lba_curr);
free(buf);
f_close(&fp);
return 0;
}
} }
res = f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL); res = f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL);
if (res && !ignoreWriteErrors) if (res)
{ {
gfx_printf(&gfx_con, "%k\nFatal error %d when writing to SD Card%k\n\n\ EPRINTFARGS("\nFatal error (%d) when writing to SD Card", res);
Press VOL to abort and try again.\nPress POWER to ignore errors (will produce a corrupt dump).\n", EPRINTF("\nPress any key and try again.");
0xFF0000FF, res, 0xFFFFFFFF);
u32 btn = btn_wait();
if (btn & BTN_POWER) free(buf);
{ f_close(&fp);
ignoreWriteErrors = 1; return 0;
}
else
{
bytesWritten = MULTIPART_SPLIT_SIZE - num * NX_EMMC_BLOCKSIZE;
currPartIdx--;
}
} }
u32 pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start); u32 pct = (u64)((u64)(lba_curr - part->lba_start) * 100u) / (u64)(part->lba_end - part->lba_start);
if (pct != prevPct) if (pct != prevPct)
@@ -868,8 +889,8 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
totalSectors -= num; totalSectors -= num;
bytesWritten += num * NX_EMMC_BLOCKSIZE; bytesWritten += num * NX_EMMC_BLOCKSIZE;
//force a flush after a lot of data if not splitting // Force a flush after a lot of data if not splitting
if (numSplitParts == 0 && bytesWritten >= MULTIPART_SPLIT_SIZE) if (numSplitParts == 0 && bytesWritten >= multipartSplitSize)
{ {
f_sync(&fp); f_sync(&fp);
bytesWritten = 0; bytesWritten = 0;
@@ -880,8 +901,8 @@ int dump_emmc_part(char *sd_path, sdmmc_storage_t *storage, emmc_part_t *part)
out:; out:;
free(buf); free(buf);
f_close(&fp); f_close(&fp);
// Partial dump done. Remove partial dump index file. // Remove partial dump index file if no fatal errors occurred.
if(partialDumpInProgress) if(isSmallSdCard)
{ {
f_unlink(partialIdxFilename); f_unlink(partialIdxFilename);
gfx_printf(&gfx_con, "\n\nYou can now join the files and get the complete raw eMMC dump."); gfx_printf(&gfx_con, "\n\nYou can now join the files and get the complete raw eMMC dump.");
@@ -901,27 +922,23 @@ typedef enum
static void dump_emmc_selected(dumpType_t dumpType) static void dump_emmc_selected(dumpType_t dumpType)
{ {
int res = 0;
u32 timer = 0; u32 timer = 0;
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount()) if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to init/mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out; goto out;
}
else gfx_puts(&gfx_con, "Checking for available free space...\n\n");
{
gfx_puts(&gfx_con, "Checking for available free space...\n");
// Get SD Card free space for partial dumping // Get SD Card free space for partial dumping
f_getfree("", &sd_fs.free_clst, NULL); f_getfree("", &sd_fs.free_clst, NULL);
}
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; sdmmc_t sdmmc;
if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4))
{ {
gfx_printf(&gfx_con, "%kFailed to init eMMC.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Failed to init eMMC.");
goto out; goto out;
} }
@@ -945,8 +962,7 @@ static void dump_emmc_selected(dumpType_t dumpType)
bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFFFFFFF); bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFFFFFFF);
sdmmc_storage_set_mmc_partition(&storage, i+1); sdmmc_storage_set_mmc_partition(&storage, i+1);
dump_emmc_part(bootPart.name, &storage, &bootPart); res = dump_emmc_part(bootPart.name, &storage, &bootPart);
gfx_putc(&gfx_con, '\n');
} }
} }
@@ -968,8 +984,7 @@ static void dump_emmc_selected(dumpType_t dumpType)
gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i++, gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i++,
part->name, part->lba_start, part->lba_end, 0xFFFFFFFF); part->name, part->lba_start, part->lba_end, 0xFFFFFFFF);
dump_emmc_part(part->name, &storage, part); res = dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
} }
} }
@@ -986,14 +1001,15 @@ static void dump_emmc_selected(dumpType_t dumpType)
gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i++, gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i++,
rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFFFFFFF); rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFFFFFFF);
dump_emmc_part(rawPart.name, &storage, &rawPart); res = dump_emmc_part(rawPart.name, &storage, &rawPart);
gfx_putc(&gfx_con, '\n');
} }
} }
} }
gfx_putc(&gfx_con, '\n');
gfx_printf(&gfx_con, "%kTime taken: %d seconds.%k\n", 0xFF00FF96, (get_tmr() - timer) / 1000000, 0xFFFFFFFF); gfx_printf(&gfx_con, "%kTime taken: %d seconds.%k\n", 0xFF00FF96, (get_tmr() - timer) / 1000000, 0xFFFFFFFF);
sdmmc_storage_end(&storage); sdmmc_storage_end(&storage);
if (res)
gfx_puts(&gfx_con, "\nDone. Press any key.\n"); gfx_puts(&gfx_con, "\nDone. Press any key.\n");
out:; out:;
@@ -1006,40 +1022,23 @@ void dump_emmc_user() { dump_emmc_selected(DUMP_USER); }
void dump_emmc_boot() { dump_emmc_selected(DUMP_BOOT); } void dump_emmc_boot() { dump_emmc_selected(DUMP_BOOT); }
void dump_emmc_rawnand() { dump_emmc_selected(DUMP_RAW); } void dump_emmc_rawnand() { dump_emmc_selected(DUMP_RAW); }
u32 save_to_file(void * buf, u32 size, const char * filename)
{
FIL fp;
if (f_open(&fp, filename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
return -1;
}
f_sync(&fp);
f_write(&fp, buf, size, NULL);
f_close(&fp);
return 0;
}
void dump_package1() void dump_package1()
{ {
u8 * pkg1 = (u8 *)malloc(0x40000); u8 * pkg1 = (u8 *)malloc(0x40000);
u8 * warmboot = (u8 *)malloc(0x40000); u8 * warmboot = (u8 *)malloc(0x40000);
u8 * secmon = (u8 *)malloc(0x40000); u8 * secmon = (u8 *)malloc(0x40000);
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount()) if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out; goto out;
}
sdmmc_storage_t storage; sdmmc_storage_t storage;
sdmmc_t sdmmc; sdmmc_t sdmmc;
if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4)) if(!sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4))
{ {
gfx_printf(&gfx_con, "%kFailed to init eMMC.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Failed to init eMMC.");
goto out; goto out;
} }
sdmmc_storage_set_mmc_partition(&storage, 1); sdmmc_storage_set_mmc_partition(&storage, 1);
@@ -1050,7 +1049,7 @@ void dump_package1()
const pk11_hdr_t *hdr = (pk11_hdr_t *)(pkg1 + pkg1_id->pkg11_off + 0x20); const pk11_hdr_t *hdr = (pk11_hdr_t *)(pkg1 + pkg1_id->pkg11_off + 0x20);
if (!pkg1_id) if (!pkg1_id)
{ {
gfx_printf(&gfx_con, "%kCould not identify package 1 version to read TSEC firmware (= '%s').%k\n", 0xFF0000FF, (char *)pkg1 + 0x10, 0xFFFFFFFF); EPRINTFARGS("Could not identify package1 version to read TSEC firmware (= '%s').", (char *)pkg1 + 0x10);
goto out; goto out;
} }
@@ -1066,26 +1065,26 @@ void dump_package1()
pkg1_unpack(warmboot, secmon, pkg1_id, pkg1); pkg1_unpack(warmboot, secmon, pkg1_id, pkg1);
// display info // display info
gfx_printf(&gfx_con, "%kSecure monitor addr: %08X\n", 0xFF0000FF, pkg1_id->secmon_base); gfx_printf(&gfx_con, "%kSecure monitor addr: %08X\n", 0xFF00FFA8, pkg1_id->secmon_base);
gfx_printf(&gfx_con, "%kSecure monitor size: %08X\n", 0xFF0000FF, hdr->sm_size); gfx_printf(&gfx_con, "%kSecure monitor size: %08X\n", 0xFF00FFA8, hdr->sm_size);
gfx_printf(&gfx_con, "%kSecure monitor off: %08X\n", 0xFF0000FF, hdr->sm_off); gfx_printf(&gfx_con, "%kSecure monitor off: %08X\n", 0xFF00FFA8, hdr->sm_off);
// dump package1 // dump package1
if (save_to_file(pkg1, 0x40000, "pkg_decr.bin") == -1) { if (sd_save_to_file(pkg1, 0x40000, "pkg_decr.bin")) {
gfx_printf(&gfx_con, "Failed to create pkg_decr.bin\n"); gfx_printf(&gfx_con, "Failed to create pkg_decr.bin\n");
goto out; goto out;
} }
gfx_puts(&gfx_con, "%kPackage1 dumped to pkg_decr.bin\n"); gfx_puts(&gfx_con, "%kpackage1 dumped to pkg_decr.bin\n");
// dump sm // dump sm
if (save_to_file(secmon, 0x40000, "sm.bin") == -1) { if (sd_save_to_file(secmon, 0x40000, "sm.bin")) {
gfx_puts(&gfx_con, "Failed to create sm.bin\n"); gfx_puts(&gfx_con, "Failed to create sm.bin\n");
goto out; goto out;
} }
gfx_puts(&gfx_con, "Secure Monitor dumped to sm.bin\n"); gfx_puts(&gfx_con, "Secure Monitor dumped to sm.bin\n");
// dump warmboot // dump warmboot
if (save_to_file(warmboot, 0x40000, "warmboot.bin") == -1) { if (sd_save_to_file(warmboot, 0x40000, "warmboot.bin")) {
gfx_puts(&gfx_con, "Failed to create warmboot.bin\n"); gfx_puts(&gfx_con, "Failed to create warmboot.bin\n");
goto out; goto out;
} }
@@ -1108,7 +1107,7 @@ void launch_firmware()
ini_sec_t *cfg_sec = NULL; ini_sec_t *cfg_sec = NULL;
LIST_INIT(ini_sections); LIST_INIT(ini_sections);
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
if (sd_mount()) if (sd_mount())
@@ -1140,20 +1139,18 @@ void launch_firmware()
return; return;
} }
else else
gfx_printf(&gfx_con, "%kNo launch configurations found.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("No launch configurations found.");
free(ments); free(ments);
} }
else else
gfx_printf(&gfx_con, "%kFailed to load 'hekate_ipl.ini'.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Failed to load 'hekate_ipl.ini'.");
} }
else
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
if (!cfg_sec) if (!cfg_sec)
gfx_printf(&gfx_con, "Using default launch configuration.\n"); gfx_printf(&gfx_con, "Using default launch configuration.\n");
if (!hos_launch(cfg_sec)) if (!hos_launch(cfg_sec))
gfx_printf(&gfx_con, "%kFailed to launch firmware.%k\n", 0xFF0000FF, 0xFFFFFFFF); EPRINTF("Failed to launch firmware.");
//TODO: free ini. //TODO: free ini.
@@ -1190,7 +1187,7 @@ void about()
" (/` ( (` ) ) '-; %k[switchbrew]%k\n" " (/` ( (` ) ) '-; %k[switchbrew]%k\n"
" ` '-; (-'%k"; " ` '-; (-'%k";
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_setpos(&gfx_con, 0, 0); gfx_con_setpos(&gfx_con, 0, 0);
gfx_printf(&gfx_con, octopus, 0xFFFFCC00, 0xFFFFFFFF, gfx_printf(&gfx_con, octopus, 0xFFFFCC00, 0xFFFFFFFF,
@@ -1220,7 +1217,7 @@ ment_t ment_tools[] = {
MDEF_HANDLER("Dump eMMC SYS", dump_emmc_system), MDEF_HANDLER("Dump eMMC SYS", dump_emmc_system),
MDEF_HANDLER("Dump eMMC USER", dump_emmc_user), MDEF_HANDLER("Dump eMMC USER", dump_emmc_user),
MDEF_HANDLER("Dump eMMC BOOT", dump_emmc_boot), MDEF_HANDLER("Dump eMMC BOOT", dump_emmc_boot),
MDEF_HANDLER("Dump Package1", dump_package1), MDEF_HANDLER("Dump package1", dump_package1),
MDEF_END() MDEF_END()
}; };
menu_t menu_tools = { menu_t menu_tools = {
@@ -1240,7 +1237,7 @@ ment_t ment_top[] = {
}; };
menu_t menu_top = { menu_t menu_top = {
ment_top, ment_top,
"hekate - ipl (CTCaer mod v1.3)", 0, 0 "hekate - ipl (CTCaer mod v1.5)", 0, 0
}; };
extern void pivot_stack(u32 stack_top); extern void pivot_stack(u32 stack_top);
@@ -1262,7 +1259,7 @@ void ipl_main()
//display_color_screen(0xAABBCCDD); //display_color_screen(0xAABBCCDD);
u32 *fb = display_init_framebuffer(); u32 *fb = display_init_framebuffer();
gfx_init_ctxt(&gfx_ctxt, fb, 720, 1280, 768); gfx_init_ctxt(&gfx_ctxt, fb, 720, 1280, 768);
gfx_clear(&gfx_ctxt, 0xFF000000); gfx_clear(&gfx_ctxt, 0xFF1B1B1B);
gfx_con_init(&gfx_con, &gfx_ctxt); gfx_con_init(&gfx_con, &gfx_ctxt);
while (1) while (1)

View File

@@ -42,6 +42,7 @@ PATCHSET_DEF(_secmon_2_patchset,
PATCHSET_DEF(_secmon_3_patchset, PATCHSET_DEF(_secmon_3_patchset,
//Patch package2 decryption and signature/hash checks. //Patch package2 decryption and signature/hash checks.
{ 0xAC8 + 0xAB4, _NOP() },
{ 0xAC8 + 0xA30, _NOP() }, //Header signature. { 0xAC8 + 0xA30, _NOP() }, //Header signature.
{ 0xAC8 + 0xAC0, _NOP() }, //Version. { 0xAC8 + 0xAC0, _NOP() }, //Version.
{ 0xAC8 + 0xADC, _NOP() } //Sections SHA2. { 0xAC8 + 0xADC, _NOP() } //Sections SHA2.
@@ -56,6 +57,7 @@ PATCHSET_DEF(_secmon_5_patchset,
); );
PATCHSET_DEF(_secmon_6_patchset, PATCHSET_DEF(_secmon_6_patchset,
//Patch package2 decryption and signature/hash checks.
{ 0x12b0 + 0x4d0, _NOP() }, { 0x12b0 + 0x4d0, _NOP() },
{ 0x12b0 + 0x4dc, _NOP() }, { 0x12b0 + 0x4dc, _NOP() },
{ 0x12b0 + 0x794, _NOP() }, { 0x12b0 + 0x794, _NOP() },
@@ -77,7 +79,7 @@ PATCHSET_DEF(_secmon_6_patchset,
static const pkg1_id_t _pkg1_ids[] = { static const pkg1_id_t _pkg1_ids[] = {
{ "20161121183008", 0, 0x1900, 0x3FE0, { 2, 1, 0 }, 0x40014020, _secmon_1_patchset }, //1.0.0 { "20161121183008", 0, 0x1900, 0x3FE0, { 2, 1, 0 }, 0x40014020, _secmon_1_patchset }, //1.0.0
{ "20170210155124", 0, 0x1900, 0x3FE0, { 0, 1, 2 }, 0x4002D000, _secmon_2_patchset }, //2.0.0 { "20170210155124", 0, 0x1900, 0x3FE0, { 0, 1, 2 }, 0x4002D000, _secmon_2_patchset }, //2.0.0
{ "20170519101410", 1, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, NULL }, //3.0.0 { "20170519101410", 1, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, _secmon_3_patchset }, //3.0.0
{ "20170710161758", 2, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, NULL }, //3.0.1 { "20170710161758", 2, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, NULL }, //3.0.1
{ "20170921172629", 3, 0x1800, 0x3FE0, { 1, 2, 0 }, 0x4002B000, _secmon_5_patchset }, //4.0.0 { "20170921172629", 3, 0x1800, 0x3FE0, { 1, 2, 0 }, 0x4002B000, _secmon_5_patchset }, //4.0.0
{ "20180220163747", 4, 0x1900, 0x3FE0, { 1, 2, 0 }, 0x4002B000, _secmon_6_patchset }, //5.0.0 { "20180220163747", 4, 0x1900, 0x3FE0, { 1, 2, 0 }, 0x4002B000, _secmon_6_patchset }, //5.0.0

View File

@@ -42,7 +42,6 @@ typedef struct _pkg1_id_t
patch_t *secmon_patchset; patch_t *secmon_patchset;
} pkg1_id_t; } pkg1_id_t;
typedef struct _pk11_hdr_t typedef struct _pk11_hdr_t
{ {
u32 magic; u32 magic;

View File

@@ -824,9 +824,22 @@ int _sd_storage_enable_highspeed_high_volt(sdmmc_storage_t *storage, u8 *buf)
static void _sd_storage_parse_ssr(sdmmc_storage_t *storage) static void _sd_storage_parse_ssr(sdmmc_storage_t *storage)
{ {
u32 *raw_ssr = (u32 *)&(storage->raw_ssr); // unstuff_bits supports only 4 u32 so break into 2 x 16byte groups
storage->ssr.bus_width = unstuff_bits(raw_ssr, 510 - 384, 2) & SD_BUS_WIDTH_4 ? 4 : 1; u32 raw_ssr1[4];
switch(unstuff_bits(raw_ssr, 440 - 384, 8)) u32 raw_ssr2[4];
raw_ssr1[3] = *(u32 *)&storage->raw_ssr[12];
raw_ssr1[2] = *(u32 *)&storage->raw_ssr[8];
raw_ssr1[1] = *(u32 *)&storage->raw_ssr[4];
raw_ssr1[0] = *(u32 *)&storage->raw_ssr[0];
raw_ssr2[3] = *(u32 *)&storage->raw_ssr[28];
raw_ssr2[2] = *(u32 *)&storage->raw_ssr[24];
raw_ssr2[1] = *(u32 *)&storage->raw_ssr[20];
raw_ssr2[0] = *(u32 *)&storage->raw_ssr[16];
storage->ssr.bus_width = unstuff_bits(raw_ssr1, 510 - 384, 2) & SD_BUS_WIDTH_4 ? 4 : 1;
switch(unstuff_bits(raw_ssr1, 440 - 384, 8))
{ {
case 0: case 0:
storage->ssr.speed_class = 0; storage->ssr.speed_class = 0;
@@ -844,12 +857,13 @@ static void _sd_storage_parse_ssr(sdmmc_storage_t *storage)
storage->ssr.speed_class = 10; storage->ssr.speed_class = 10;
break; break;
default: default:
storage->ssr.speed_class = unstuff_bits(raw_ssr, 440 - 384, 8); storage->ssr.speed_class = unstuff_bits(raw_ssr1, 440 - 384, 8);
break; break;
} }
storage->ssr.uhs_grade = unstuff_bits(raw_ssr, 396 - 384, 4); storage->ssr.uhs_grade = unstuff_bits(raw_ssr1, 396 - 384, 4);
storage->ssr.video_class = unstuff_bits(raw_ssr, 384 - 384, 8); storage->ssr.video_class = unstuff_bits(raw_ssr1, 384 - 384, 8);
storage->ssr.app_class = unstuff_bits(raw_ssr + 16, 472 + 4 - 384, 4);
storage->ssr.app_class = unstuff_bits(raw_ssr2, 336 - 256, 4);
} }
static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf) static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf)
@@ -914,6 +928,7 @@ static void _sd_storage_parse_csd(sdmmc_storage_t *storage)
storage->csd.structure = unstuff_bits(raw_csd, 126, 2); storage->csd.structure = unstuff_bits(raw_csd, 126, 2);
storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12); storage->csd.cmdclass = unstuff_bits(raw_csd, 84, 12);
storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4); storage->csd.read_blkbits = unstuff_bits(raw_csd, 80, 4);
storage->csd.write_protect = unstuff_bits(raw_csd, 12, 2);
switch(storage->csd.structure) switch(storage->csd.structure)
{ {
case 0: case 0:

View File

@@ -46,6 +46,7 @@ typedef struct _mmc_csd
u32 read_blkbits; u32 read_blkbits;
u32 write_blkbits; u32 write_blkbits;
u32 capacity; u32 capacity;
u8 write_protect;
} mmc_csd_t; } mmc_csd_t;
typedef struct _mmc_ext_csd typedef struct _mmc_ext_csd

View File

@@ -26,4 +26,5 @@ void se_aes_key_clear(u32 ks);
int se_aes_unwrap_key(u32 ks_dst, u32 ks_src, const void *input); int se_aes_unwrap_key(u32 ks_dst, u32 ks_src, const void *input);
int se_aes_crypt_block_ecb(u32 ks, u32 enc, void *dst, const void *src); int se_aes_crypt_block_ecb(u32 ks, u32 enc, void *dst, const void *src);
int se_aes_crypt_ctr(u32 ks, void *dst, u32 dst_size, const void *src, u32 src_size, void *ctr); int se_aes_crypt_ctr(u32 ks, void *dst, u32 dst_size, const void *src, u32 src_size, void *ctr);
#endif #endif

View File

@@ -16,6 +16,7 @@
#include "tui.h" #include "tui.h"
#include "btn.h" #include "btn.h"
#include "ctc_logo.h"
void tui_pbar(gfx_con_t *con, int x, int y, u32 val) void tui_pbar(gfx_con_t *con, int x, int y, u32 val)
{ {
@@ -42,11 +43,12 @@ void *tui_do_menu(gfx_con_t *con, menu_t *menu)
{ {
int idx = 0, cnt; int idx = 0, cnt;
gfx_clear(con->gfx_ctxt, 0xFF000000); gfx_clear(con->gfx_ctxt, 0xFF1B1B1B);
gfx_set_logo(con->gfx_ctxt, 538, 1180, LOGO_WIDTH, LOGO_HEIGHT, CTC_LOGO);
while (1) while (1)
{ {
gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF000000); gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF1B1B1B);
gfx_con_setpos(con, menu->x, menu->y); gfx_con_setpos(con, menu->x, menu->y);
gfx_printf(con, "[%s]\n\n", menu->caption); gfx_printf(con, "[%s]\n\n", menu->caption);
@@ -55,7 +57,7 @@ void *tui_do_menu(gfx_con_t *con, menu_t *menu)
if (cnt == idx) if (cnt == idx)
gfx_con_setcol(con, 0xFF000000, 1, 0xFFCCCCCC); gfx_con_setcol(con, 0xFF000000, 1, 0xFFCCCCCC);
else else
gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF000000); gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF1B1B1B);
con->x += 8; con->x += 8;
gfx_printf(con, "%s", menu->ents[cnt].caption); gfx_printf(con, "%s", menu->ents[cnt].caption);
if(menu->ents[cnt].type == MENT_MENU) if(menu->ents[cnt].type == MENT_MENU)
@@ -63,15 +65,19 @@ void *tui_do_menu(gfx_con_t *con, menu_t *menu)
gfx_putc(con, '\n'); gfx_putc(con, '\n');
} }
gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF000000); gfx_con_setcol(con, 0xFFFFFFFF, 1, 0xFF1B1B1B);
gfx_putc(con, '\n'); gfx_putc(con, '\n');
u32 btn = btn_wait(); u32 btn = btn_wait();
if (btn & BTN_VOL_DOWN && idx < cnt - 1) if (btn & BTN_VOL_DOWN && idx < (cnt - 1))
idx++; idx++;
else if (btn & BTN_VOL_DOWN && idx == (cnt - 1))
idx = 0;
if (btn & BTN_VOL_UP && idx > 0) if (btn & BTN_VOL_UP && idx > 0)
idx--; idx--;
else if (btn & BTN_VOL_UP && idx == 0)
idx = cnt - 1;
if (btn & BTN_POWER) if (btn & BTN_POWER)
{ {
ment_t *ent = &menu->ents[idx]; ment_t *ent = &menu->ents[idx];
@@ -90,7 +96,8 @@ void *tui_do_menu(gfx_con_t *con, menu_t *menu)
return NULL; return NULL;
break; break;
} }
gfx_clear(con->gfx_ctxt, 0xFF000000); gfx_clear(con->gfx_ctxt, 0xFF1B1B1B);
gfx_set_logo(con->gfx_ctxt, 538, 1180, LOGO_WIDTH, LOGO_HEIGHT, CTC_LOGO);
} }
} }