Compare commits

..

15 Commits

Author SHA1 Message Date
Kostas Missos
40938fdb58 Add partial support for unknown CSD Structure V3 2018-05-06 20:59:46 +03:00
Kostas Missos
73e3cc7be6 Make write error fatal but let user choose to continue
Write error to sd is now fatal as per FatFs guidelines.
Let the user choose to continue or to abort and try again.
Add a message to let the user know that we calculate free space

Signed-off-by: Kostas Missos <ctcaer@gmail.com>
2018-05-06 20:52:47 +03:00
Kostas Missos
90f893f573 Fix small typo
Signed-off-by: Kostas Missos <ctcaer@gmail.com>
2018-05-06 20:52:47 +03:00
Kostas Missos
3f44441df4 Add SD write retry and some other changes
Increase retry count to 6 tries
Add colors and change a little bit some messages
Change some namings

Signed-off-by: Kostas Missos <ctcaer@gmail.com>
2018-05-06 20:52:47 +03:00
Kostas Missos
44c3ae5ef1 Add partial dumping when free space is not enough
Signed-off-by: Kostas Missos <ctcaer@gmail.com>
2018-05-06 20:52:47 +03:00
Kostas Missos
5a775ebb72 Add lv support back and fix its change logic 2018-05-06 20:26:30 +03:00
Kostas Missos
884ad1f6e6 Add support for SD cards that report wrong info
Some vendors, *I'm looking at you Samsung*, report support for 1.8 voltage, even though the card does not support it.

So, disable low voltage support for now until the behavior is changed to act more like what Linux does. (re-initializing the SD card and disables the low voltage switch).

Fix the switch to high speed mode for high voltages also.

Additionally, correct the IF COND reply. 0xAA is the important part.
2018-05-06 10:53:35 +03:00
st4rk
8d6b6c4f0e Fix mistake where I was reading pkg1 twice 2018-05-04 00:32:28 -07:00
st4rk
6ebfaa1200 keygen for 3.0.x, 4.0.x, 5.0.x added, 3.0.X FW booting, fixed TSEC wrong offset for 4.0.X 2018-05-04 00:31:08 -07:00
st4rk
8b9e65e76f keygen for 3.0.x, 4.0.x, 5.0.x added, 3.0.X FW booting, fixed TSEC wrong offset for 4.0.X 2018-05-04 00:15:29 -07:00
Rajko Stojadinovic
e7373548fa Make it smaller 2018-05-02 15:37:15 +12:00
Rajko Stojadinovic
138a7658ad Add missing const on constants 2018-05-02 15:37:15 +12:00
Rajko Stojadinovic
8a327030bd Enable exFAT support, don't split files if using it (fsync them instead at 2GiB boundaries), add RawNand dump option 2018-05-02 15:37:15 +12:00
Rajko Stojadinovic
ee30961b3a Fix percentage overflow when dumping USER, also add retry in case of read failure (experienced by some users) 2018-05-02 15:37:15 +12:00
Rajko Stojadinovic
04de3d184a Support dumping all possible eMMC partitions (with file splitting when necessary) 2018-05-02 15:37:15 +12:00
8 changed files with 509 additions and 421 deletions

View File

@@ -44,7 +44,7 @@ OBJS = $(addprefix $(BUILD)/, \
OBJS += $(addprefix $(BUILD)/, diskio.o ff.o ffunicode.o)
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb -mthumb-interwork
CFLAGS = $(ARCH) -O2 -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11# -Wall
CFLAGS = $(ARCH) -Os -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-inline -std=gnu11# -Wall
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections
.PHONY: all clean

View File

@@ -200,7 +200,7 @@
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 0
#define FF_FS_NOFSINFO 1
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
@@ -224,7 +224,7 @@
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ When enable exFAT, also LFN needs to be enabled.
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */

227
ipl/hos.c
View File

@@ -59,6 +59,13 @@ static const u8 ckey_keyseed[0x10] =
static const u8 key8_keyseed[] =
{ 0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7 };
static const u8 new_masterkey_seed[0x10] =
{ 0x2D, 0xC1, 0xF4, 0x8D, 0xF3, 0x5B, 0x69, 0x33, 0x42, 0x10, 0xAC, 0x65, 0xDA, 0x90, 0x46, 0x66 };
static const u8 new_per_console_key[0x10] =
{ 0x0C, 0x91, 0x09, 0xDB, 0x93, 0x93, 0x07, 0x81, 0x07, 0x3C, 0xC4, 0x16, 0x22, 0x7C, 0x6C, 0x28 };
static void _se_lock()
{
for (u32 i = 0; i < 16; i++)
@@ -86,8 +93,8 @@ static void _se_lock()
gfx_hexdump(&gfx_con, SE_BASE, (void *)SE_BASE, 0x400);*/
}
//Key derivation for < 4.0.0
static int _keygen_1(u8 *keyblob, u32 kb, void *tsec_fw)
// <-- key derivation algorithm
static int keygen(u8 *keyblob, u32 kb, void *tsec_fw)
{
u8 *tmp = (u8 *)malloc(0x10);
@@ -97,13 +104,8 @@ static int _keygen_1(u8 *keyblob, u32 kb, void *tsec_fw)
//Get TSEC key.
if (tsec_query(tmp, 1, tsec_fw) < 0)
return 0;
se_aes_key_set(13, tmp, 0x10);
//Derive keyblob key from TSEC+SBK.
memcpy(tmp, keyblob_keyseeds[kb], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(13, 14, tmp);
se_aes_key_clear(14);
se_aes_key_set(13, tmp, 0x10);
//TODO: verify keyblob CMAC.
//se_aes_unwrap_key(11, 13, cmac_keyseed);
@@ -111,33 +113,134 @@ static int _keygen_1(u8 *keyblob, u32 kb, void *tsec_fw)
//if (!memcmp(keyblob, tmp, 0x10))
// return 0;
//Decrypt keyblob and set keyslots.
se_aes_crypt_ctr(13, keyblob + 0x20, 0x90, keyblob + 0x20, 0x90, keyblob + 0x10);
se_aes_key_set(11, keyblob + 0x20 + 0x80, 0x10);
se_aes_key_set(12, keyblob + 0x20, 0x10);
switch(kb) {
// 1.0.0~2.0.0 FW
case 0: {
//TODO: for some reason SE likes to hang if we don't execute an operation here.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_crypt_block_ecb(12, 0, tmp, tmp);
//Derive keyblob key from TSEC+SBK.
memcpy(tmp, keyblob_keyseeds[kb], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(13, 14, tmp);
se_aes_key_clear(14);
//Generate retail master key.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_unwrap_key(12, 12, tmp);
se_aes_crypt_ctr(13, keyblob + 0x20, 0x90, keyblob + 0x20, 0x90, keyblob + 0x10);
se_aes_key_set(11, keyblob + 0x20 + 0x80, 0x10);
se_aes_key_set(12, keyblob + 0x20, 0x10);
memcpy(tmp, key8_keyseed, 0x10);
se_key_acc_ctrl(8, 0x15);
se_aes_unwrap_key(8, 12, tmp);
//TODO: for some reason SE likes to hang if we don't execute an operation here.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_crypt_block_ecb(12, 0, tmp, tmp);
//Generate console specific key.
memcpy(tmp, ckey_keyseed, 0x10);
se_aes_unwrap_key(13, 13, tmp);
//Generate retail master key.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_unwrap_key(12, 12, tmp);
se_key_acc_ctrl(12, 0xFF);
se_key_acc_ctrl(13, 0xFF);
//Generate console specific key.
memcpy(tmp, ckey_keyseed, 0x10);
se_aes_unwrap_key(13, 13, tmp);
memcpy(tmp, key8_keyseed, 0x10);
se_key_acc_ctrl(8, 0x15);
se_aes_unwrap_key(8, 12, tmp);
se_key_acc_ctrl(12, 0xFF);
se_key_acc_ctrl(13, 0xFF);
}
break;
// 3.0.0~3.0.1 FW
case 1:
case 2: {
// keyslot 10
memcpy(tmp, keyblob_keyseeds[0], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(10, 14, tmp);
// keyslot 13
memcpy(tmp, keyblob_keyseeds[kb], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(13, 14, tmp);
se_aes_key_clear(14);
se_aes_key_clear(15);
se_aes_crypt_ctr(13, keyblob + 0x20, 0x90, keyblob + 0x20, 0x90, keyblob + 0x10);
se_aes_key_set(11, keyblob + 0x20 + 0x80, 0x10);
se_aes_key_set(12, keyblob + 0x20, 0x10);
//TODO: for some reason SE likes to hang if we don't execute an operation here.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_crypt_block_ecb(12, 0, tmp, tmp);
//Generate retail master key.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_unwrap_key(12, 12, tmp);
//Generate console specific key.
memcpy(tmp, ckey_keyseed, 0x10);
se_aes_unwrap_key(13, 10, tmp);
se_aes_key_clear(10);
memcpy(tmp, key8_keyseed, 0x10);
se_key_acc_ctrl(8, 0x15);
se_aes_unwrap_key(8, 12, tmp);
se_key_acc_ctrl(12, 0xFF);
se_key_acc_ctrl(13, 0xFF);
}
break;
// 4.0.0~5.0.1 FW
case 3:
case 4: {
se_key_acc_ctrl(14, 0x15);
se_key_acc_ctrl(15, 0x15);
// keyslot 15
memcpy(tmp, keyblob_keyseeds[0], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(15, 14, tmp);
// keyslot 13
memcpy(tmp, keyblob_keyseeds[kb], 0x10);
se_aes_crypt_block_ecb(13, 0, tmp, tmp);
se_aes_unwrap_key(13, 14, tmp);
se_aes_key_clear(14);
se_aes_crypt_ctr(13, keyblob + 0x20, 0x90, keyblob + 0x20, 0x90, keyblob + 0x10);
se_aes_key_set(11, keyblob + 0x20 + 0x80, 0x10);
se_aes_key_set(12, keyblob + 0x20, 0x10);
//TODO: for some reason SE likes to hang if we don't execute an operation here.
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_crypt_block_ecb(12, 0, tmp, tmp);
// keyslot 14
memcpy(tmp, new_masterkey_seed, 0x10);
se_aes_unwrap_key(14, 12, tmp);
// keyslot 12
memcpy(tmp, mkey_keyseed_retail, 0x10);
se_aes_unwrap_key(12, 12, tmp);
// keyslot 13
memcpy(tmp, new_per_console_key, 0x10);
se_aes_unwrap_key(13, 15, tmp);
// keyslot 15
memcpy(tmp, ckey_keyseed, 0x10);
se_aes_unwrap_key(15, 13, tmp);
se_key_acc_ctrl(12, 0xFF);
se_key_acc_ctrl(15, 0xFF);
}
break;
}
free(tmp);
}
typedef struct _launch_ctxt_t
{
void *keyblob;
@@ -325,12 +428,12 @@ int hos_launch(ini_sec_t *cfg)
return 0;
//XXX: remove this once we support 3+.
if (ctxt.pkg1_id->kb > 0)
return 0;
//if (ctxt.pkg1_id->kb > 0)
// return 0;
DPRINTF("loaded pkg1 and keyblob\n");
//Generate keys.
_keygen_1(ctxt.keyblob, ctxt.pkg1_id->kb, (u8 *)ctxt.pkg1 + ctxt.pkg1_id->tsec_off);
keygen(ctxt.keyblob, ctxt.pkg1_id->kb, (u8 *)ctxt.pkg1 + ctxt.pkg1_id->tsec_off);
DPRINTF("generated keys\n");
//Decrypt and unpack package1 if we require parts of it.
if (!ctxt.warmboot || !ctxt.secmon)
@@ -347,41 +450,57 @@ DPRINTF("decrypted and unpacked pkg1\n");
//Set warmboot address in PMC.
PMC(APBDEV_PMC_SCRATCH1) = 0x8000D000;
//Replace 'SecureMonitor' if requested.
if (ctxt.secmon)
if (ctxt.secmon) {
memcpy((void *)ctxt.pkg1_id->secmon_base, ctxt.secmon, ctxt.secmon_size);
}
else
{
//Else we patch it to allow for an unsigned package2.
patch_t *secmon_patchset = ctxt.pkg1_id->secmon_patchset;
for (u32 i = 0; secmon_patchset[i].off != 0xFFFFFFFF; i++)
*(vu32 *)(ctxt.pkg1_id->secmon_base + secmon_patchset[i].off) = secmon_patchset[i].val;
}
DPRINTF("loaded warmboot.bin and secmon\n");
if (secmon_patchset != NULL) {
for (u32 i = 0; secmon_patchset[i].off != 0xFFFFFFFF; i++)
*(vu32 *)(ctxt.pkg1_id->secmon_base + secmon_patchset[i].off) = secmon_patchset[i].val;
//Read package2.
if (!_read_emmc_pkg2(&ctxt))
return 0;
DPRINTF("read pkg2\n");
//Decrypt package2 and parse KIP1 blobs in INI1 section.
pkg2_hdr_t *pkg2_hdr = pkg2_decrypt(ctxt.pkg2);
DPRINTF("loaded warmboot.bin and secmon\n");
//Read package2.
if (!_read_emmc_pkg2(&ctxt))
return 0;
LIST_INIT(kip1_info);
pkg2_parse_kips(&kip1_info, pkg2_hdr);
DPRINTF("parsed ini1\n");
//Use the kernel included in package2 in case we didn't load one already.
if (!ctxt.kernel)
{
ctxt.kernel = pkg2_hdr->data;
ctxt.kernel_size = pkg2_hdr->sec_size[PKG2_SEC_KERNEL];
DPRINTF("read pkg2\n");
//Decrypt package2 and parse KIP1 blobs in INI1 section.
pkg2_hdr_t *pkg2_hdr = pkg2_decrypt(ctxt.pkg2);
LIST_INIT(kip1_info);
pkg2_parse_kips(&kip1_info, pkg2_hdr);
DPRINTF("parsed ini1\n");
//Use the kernel included in package2 in case we didn't load one already.
if (!ctxt.kernel)
{
ctxt.kernel = pkg2_hdr->data;
ctxt.kernel_size = pkg2_hdr->sec_size[PKG2_SEC_KERNEL];
}
//Merge extra KIP1s into loaded ones.
LIST_FOREACH_ENTRY(merge_kip_t, mki, &ctxt.kip1_list, link)
pkg2_merge_kip(&kip1_info, (pkg2_kip1_t *)mki->kip1);
//Rebuild and encrypt package2.
pkg2_build_encrypt((void *)0xA9800000, ctxt.kernel, ctxt.kernel_size, &kip1_info);
DPRINTF("rebuilt pkg2\n");
} else {
//Read package2.
if (!_read_emmc_pkg2(&ctxt))
return 0;
DPRINTF("read pkg2\n");
memcpy((void *)0xA9800000, ctxt.pkg2, ctxt.pkg2_size);
}
}
//Merge extra KIP1s into loaded ones.
LIST_FOREACH_ENTRY(merge_kip_t, mki, &ctxt.kip1_list, link)
pkg2_merge_kip(&kip1_info, (pkg2_kip1_t *)mki->kip1);
//Rebuild and encrypt package2.
pkg2_build_encrypt((void *)0xA9800000, ctxt.kernel, ctxt.kernel_size, &kip1_info);
DPRINTF("rebuilt pkg2\n");
//Clear 'BootConfig'.
memset((void *)0x4003D000, 0, 0x3000);

View File

@@ -15,6 +15,8 @@
*/
#include <string.h>
#include <stdlib.h>
#include <alloca.h>
#include "clock.h"
#include "uart.h"
@@ -169,6 +171,10 @@ void config_se_brom()
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_0) = 0x0;
PMC(APBDEV_PMC_SCRATCH49_0) = 0x0;
}
void config_hw()
@@ -380,40 +386,247 @@ void *sd_file_read(char *path)
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 MULTIPART_SPLIT_SIZE = (1u << 31);
static const u32 SECTORS_TO_MB_COEFF = 0x800;
u32 totalSectors = part->lba_end - part->lba_start + 1;
u32 currPartIdx = 0;
u32 numSplitParts = 0;
u32 maxSplitParts = 0;
int isSmallSdCard = 0;
int partialDumpInProgress = 0;
int res = 0;
int ignoreWriteErrors = 0;
char* outFilename = sd_path;
u32 sdPathLen = strlen(sd_path);
FIL partialIdxFp;
char partialIdxFilename[12];
memcpy(partialIdxFilename, "partial.idx", 11);
partialIdxFilename[11] = 0;
gfx_printf(&gfx_con, "SD Card free space: %dMB, Total dump size %dMB\n",
sd_fs.free_clst * sd_fs.csize / SECTORS_TO_MB_COEFF,
totalSectors / SECTORS_TO_MB_COEFF);
// 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 = 1;
gfx_printf(&gfx_con, "%kSD 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)
{
gfx_printf(&gfx_con, "%kNot enough free space for partial dumping.%k\n", 0xFF0000FF, 0xFFFFFFFF);
return 0;
}
}
// Check if we continueing a previous raw eMMC dump in progress.
if (isSmallSdCard)
{
if (f_open(&partialIdxFp, partialIdxFilename, FA_READ) == FR_OK)
{
gfx_printf(&gfx_con, "%kFound partial dump in progress. Continuing...%k\n", 0xFF14FDAE, 0xFFFFFFFF);
partialDumpInProgress = 1;
f_read(&partialIdxFp, &currPartIdx, 4, NULL);
f_close(&partialIdxFp);
// Increase maxSplitParts to accommodate previously dumped parts
maxSplitParts += currPartIdx;
}
else
gfx_printf(&gfx_con, "%kContinuing with partial dumping...%k\n", 0xFF00BAFF, 0xFFFFFFFF);
}
// Check if filesystem is FAT32 or the free space is smaller and dump in parts
if (((sd_fs.fs_type != FS_EXFAT) || isSmallSdCard) && totalSectors > (FAT32_FILESIZE_LIMIT/NX_EMMC_BLOCKSIZE))
{
static const u32 MULTIPART_SPLIT_SECTORS = MULTIPART_SPLIT_SIZE/NX_EMMC_BLOCKSIZE;
numSplitParts = (totalSectors+MULTIPART_SPLIT_SECTORS-1)/MULTIPART_SPLIT_SECTORS;
outFilename = alloca(sdPathLen+4);
memcpy(outFilename, sd_path, sdPathLen);
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 dump in proggress.
else
{
if (numSplitParts >= 10 && currPartIdx < 10)
{
outFilename[sdPathLen] = '0';
itoa(currPartIdx, &outFilename[sdPathLen+1], 10);
}
else
itoa(currPartIdx, &outFilename[sdPathLen], 10);
}
}
FIL fp;
if (f_open(&fp, sd_path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
return 0;
u8 *buf = (u8 *)malloc(NX_EMMC_BLOCKSIZE * 512);
static const u32 NUM_SECTORS_PER_ITER = 512;
u8 *buf = (u8 *)malloc(NX_EMMC_BLOCKSIZE * NUM_SECTORS_PER_ITER);
u32 total = part->lba_end - part->lba_start + 1;
u32 lba_curr = part->lba_start;
while(total > 0)
{
u32 num = MIN(total, 512);
u32 bytesWritten = 0;
u32 prevPct = 200;
int retryCount = 0;
if(!sdmmc_storage_read(storage, lba_curr, num, buf))
// Continue from where we left, if partial dump in proggress.
if (partialDumpInProgress)
{
lba_curr += currPartIdx * (MULTIPART_SPLIT_SIZE / NX_EMMC_BLOCKSIZE);
totalSectors -= currPartIdx * (MULTIPART_SPLIT_SIZE / NX_EMMC_BLOCKSIZE);
}
while(totalSectors > 0)
{
if (numSplitParts != 0 && bytesWritten >= MULTIPART_SPLIT_SIZE)
{
gfx_printf(&gfx_con, "%kError reading %d blocks @ LBA %08X%k\n",
0xFF0000FF, num, lba_curr, 0xFFFFFFFF);
goto out;
f_close(&fp);
memset(&fp, 0, sizeof(fp));
currPartIdx++;
if (numSplitParts >= 10 && currPartIdx < 10)
{
outFilename[sdPathLen] = '0';
itoa(currPartIdx, &outFilename[sdPathLen+1], 10);
}
else
itoa(currPartIdx, &outFilename[sdPathLen], 10);
// More parts to dump that do not currently fit the sd card free space
if ((isSmallSdCard && currPartIdx >= maxSplitParts) || (res && !ignoreWriteErrors))
{
// Create partial dump 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_printf(&gfx_con, "%k\nError creating partial.idx file.%k\n", 0xFF0000FF, 0xFFFFFFFF);
free(buf);
return 0;
}
if (res && !ignoreWriteErrors)
{
gfx_printf(&gfx_con, "%k\nPress any key and try again.%k\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 \
Don\'t move the partial.idx file!\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");
free(buf);
return 1;
}
if (f_open(&fp, outFilename, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK)
{
free(buf);
return 0;
}
bytesWritten = 0;
}
f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL);
u32 pct = ((lba_curr - part->lba_start) * 100) / (part->lba_end - part->lba_start);
tui_pbar(&gfx_con, 0, gfx_con.y, pct);
retryCount = 0;
u32 num = MIN(totalSectors, NUM_SECTORS_PER_ITER);
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",
0xFF0000FF, num, lba_curr, ++retryCount, 0xFFFFFFFF);
sleep(500000);
if (retryCount >= 10)
goto out;
}
res = f_write(&fp, buf, NX_EMMC_BLOCKSIZE * num, NULL);
if (res && !ignoreWriteErrors)
{
gfx_printf(&gfx_con, "%kFatal error %d when writing to SD Card%k\n\
Press VOL to abort and try again\nPress POWER to ignore errors (will produce a corrupt dump)",
0xFF0000FF, res, 0xFFFFFFFF);
u32 btn = btn_wait();
if (btn & BTN_POWER)
{
bytesWritten = MULTIPART_SPLIT_SIZE - num * NX_EMMC_BLOCKSIZE;
currPartIdx--;
}
else
{
ignoreWriteErrors = 1;
}
}
u32 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);
prevPct = pct;
}
lba_curr += num;
total -= num;
totalSectors -= num;
bytesWritten += num * NX_EMMC_BLOCKSIZE;
//force a flush after a lot of data if not splitting
if (numSplitParts == 0 && bytesWritten >= MULTIPART_SPLIT_SIZE)
{
f_sync(&fp);
bytesWritten = 0;
}
}
tui_pbar(&gfx_con, 0, gfx_con.y, 100);
out:;
free(buf);
f_close(&fp);
// Partial dump done. Remove partial dump index file.
if(partialDumpInProgress)
{
f_unlink(partialIdxFilename);
gfx_printf(&gfx_con, "\n\nYou can now join the files and get the complete raw eMMC dump.\n");
}
return 1;
}
void dump_emmc()
typedef enum
{
DUMP_BOOT = 1,
DUMP_SYSTEM = 2,
DUMP_USER = 4,
DUMP_RAW = 8
} dumpType_t;
static void dump_emmc_selected(dumpType_t dumpType)
{
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
@@ -423,6 +636,12 @@ void dump_emmc()
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
else
{
gfx_puts(&gfx_con, "Checking for available free space...\n");
// Get SD Card free space for partial dumping
f_getfree("", &sd_fs.free_clst, NULL);
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
@@ -431,343 +650,85 @@ void dump_emmc()
gfx_printf(&gfx_con, "%kFailed to init eMMC.%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 0);
LIST_INIT(gpt);
nx_emmc_gpt_parse(&gpt, &storage);
int i = 0;
LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link)
{
gfx_printf(&gfx_con, "%02d: %s (%08X-%08X)\n", i++,
part->name, part->lba_start, part->lba_end);
if (dumpType & DUMP_BOOT)
{
static const u32 BOOT_PART_SIZE = 0x400000;
//XXX: skip these for now.
if (//!strcmp(part->name, "SYSTEM") ||
!strcmp(part->name, "USER"))
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++)
{
gfx_puts(&gfx_con, "Skipped.\n");
continue;
memcpy(bootPart.name, "BOOT", 4);
bootPart.name[4] = (u8)('0' + i);
bootPart.name[5] = 0;
gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i,
bootPart.name, bootPart.lba_start, bootPart.lba_end, 0xFFFFFFFF);
sdmmc_storage_set_mmc_partition(&storage, i+1);
dump_emmc_part(bootPart.name, &storage, &bootPart);
gfx_putc(&gfx_con, '\n');
}
}
if ((dumpType & DUMP_SYSTEM) || (dumpType & DUMP_USER) || (dumpType & DUMP_RAW))
{
sdmmc_storage_set_mmc_partition(&storage, 0);
if ((dumpType & DUMP_SYSTEM) || (dumpType & DUMP_USER))
{
LIST_INIT(gpt);
nx_emmc_gpt_parse(&gpt, &storage);
LIST_FOREACH_ENTRY(emmc_part_t, part, &gpt, link)
{
if ((dumpType & DUMP_USER) == 0 && !strcmp(part->name, "USER"))
continue;
if ((dumpType & DUMP_SYSTEM) == 0 && strcmp(part->name, "USER"))
continue;
gfx_printf(&gfx_con, "%k%02d: %s (%08X-%08X)%k\n", 0xFFFFDD00, i++,
part->name, part->lba_start, part->lba_end, 0xFFFFFFFF);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
}
}
if (dumpType & DUMP_RAW)
{
static const u32 RAW_AREA_NUM_SECTORS = 0x3A3E000;
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
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 (%08X-%08X)%k\n", 0xFFFFDD00, i++,
rawPart.name, rawPart.lba_start, rawPart.lba_end, 0xFFFFFFFF);
dump_emmc_part(rawPart.name, &storage, &rawPart);
gfx_putc(&gfx_con, '\n');
}
}
}
sdmmc_storage_end(&storage);
gfx_puts(&gfx_con, "Done. Press any key.\n");
out:;
sleep(100000);
btn_wait();
}
void dump_boot0_emmc()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 1);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x62;
part->name[1] = 0x6f;
part->name[2] = 0x6f;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x30;
part->lba_start = 0x0000;
part->lba_end = 0x1FFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_boot1_emmc()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 2);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x62;
part->name[1] = 0x6f;
part->name[2] = 0x6f;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x31;
part->lba_start = 0x0000;
part->lba_end = 0x1FFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_raw_emmc()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 0);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x70;
part->name[1] = 0x61;
part->name[2] = 0x72;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x30;
part->lba_start = 0x000000;
part->lba_end = 0x7FFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
part->name[5] = 0x31;
part->lba_start = 0x800000;
part->lba_end = 0xFFFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_raw_emmc1()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 0);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x70;
part->name[1] = 0x61;
part->name[2] = 0x72;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x32;
part->lba_start = 0x1000000;
part->lba_end = 0x17FFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
part->name[5] = 0x33;
part->lba_start = 0x1800000;
part->lba_end = 0x1FFFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_raw_emmc2()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 0);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x70;
part->name[1] = 0x61;
part->name[2] = 0x72;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x34;
part->lba_start = 0x2000000;
part->lba_end = 0x27FFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
part->name[5] = 0x35;
part->lba_start = 0x2800000;
part->lba_end = 0x2FFFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_raw_emmc3()
{
emmc_part_t *part = (emmc_part_t *)malloc(sizeof(emmc_part_t));
gfx_clear(&gfx_ctxt, 0xFF000000);
gfx_con_setpos(&gfx_con, 0, 0);
if (!sd_mount())
{
gfx_printf(&gfx_con, "%kFailed to mount SD card (make sure that it is inserted).%k\n", 0xFF0000FF, 0xFFFFFFFF);
goto out;
}
sdmmc_storage_t storage;
sdmmc_t sdmmc;
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);
goto out;
}
sdmmc_storage_set_mmc_partition(&storage, 0);
//LIST_INIT(gpt);
//nx_emmc_gpt_parse(&gpt, &storage);
part->name[0] = 0x70;
part->name[1] = 0x61;
part->name[2] = 0x72;
part->name[3] = 0x74;
part->name[4] = 0x5f;
part->name[6] = 0x0;
part->name[5] = 0x36;
part->lba_start = 0x3000000;
part->lba_end = 0x37FFFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
part->name[5] = 0x37;
part->lba_start = 0x3800000;
part->lba_end = 0x3A3DFFF;
gfx_printf(&gfx_con, "%s (%08X-%08X)\n",
part->name, part->lba_start, part->lba_end);
dump_emmc_part(part->name, &storage, part);
gfx_putc(&gfx_con, '\n');
sdmmc_storage_end(&storage);
out:;
sleep(100000);
btn_wait();
}
void dump_emmc_system() { dump_emmc_selected(DUMP_SYSTEM); }
void dump_emmc_user() { dump_emmc_selected(DUMP_USER); }
void dump_emmc_boot() { dump_emmc_selected(DUMP_BOOT); }
void dump_emmc_rawnand() { dump_emmc_selected(DUMP_RAW); }
void launch_firmware()
{
@@ -878,25 +839,12 @@ menu_t menu_cinfo = {
"Console info", 0, 0
};
ment_t ment_rawemmc[] = {
MDEF_BACK(),
MDEF_HANDLER("Boot0", dump_boot0_emmc),
MDEF_HANDLER("Boot1", dump_boot1_emmc),
MDEF_HANDLER("1st 8GB", dump_raw_emmc),
MDEF_HANDLER("2nd 8GB", dump_raw_emmc1),
MDEF_HANDLER("3rd 8GB", dump_raw_emmc2),
MDEF_HANDLER("4th 8GB", dump_raw_emmc3),
MDEF_END()
};
menu_t menu_rawemmc = {
ment_rawemmc,
"Dump raw eMMC", 0, 0
};
ment_t ment_tools[] = {
MDEF_BACK(),
MDEF_HANDLER("Dump eMMC", dump_emmc),
MDEF_MENU("Dump raw eMMC", &menu_rawemmc),
MDEF_HANDLER("Dump RAW eMMC", dump_emmc_rawnand),
MDEF_HANDLER("Dump eMMC SYS", dump_emmc_system),
MDEF_HANDLER("Dump eMMC USER", dump_emmc_user),
MDEF_HANDLER("Dump eMMC BOOT", dump_emmc_boot),
MDEF_END()
};
menu_t menu_tools = {

View File

@@ -40,6 +40,13 @@ PATCHSET_DEF(_secmon_2_patchset,
{ 0xAC8 + 0xB58, _NOP() } //Sections SHA2.
);
PATCHSET_DEF(_secmon_3_patchset,
//Patch package2 decryption and signature/hash checks.
{ 0xAC8 + 0xA30, _NOP() }, //Header signature.
{ 0xAC8 + 0xAC0, _NOP() }, //Version.
{ 0xAC8 + 0xADC, _NOP() } //Sections SHA2.
);
/*
* package1.1 header: <wb, ldr, sm>
* package1.1 layout:
@@ -56,7 +63,7 @@ static const pkg1_id_t _pkg1_ids[] = {
{ "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
{ "20170710161758", 2, 0x1A00, 0x3FE0, { 0, 1, 2 }, 0x4002D000, NULL }, //3.0.1
{ "20170921172629", 3, 0x1900, 0x3FE0, { 1, 2, 0 }, 0x4002B000, NULL }, //4.0.0
{ "20170921172629", 3, 0x1800, 0x3FE0, { 1, 2, 0 }, 0x4002B000, NULL }, //4.0.0
{ "20180220163747", 4, 0x1900, 0x3FE0, { 1, 2, 0 }, 0x4002B000, NULL }, //5.0.0
{ NULL, 0, 0, 0, 0 } //End.
};

View File

@@ -41,5 +41,8 @@
#define APBDEV_PMC_SCRATCH188 0x810
#define APBDEV_PMC_SCRATCH190 0x818
#define APBDEV_PMC_SCRATCH200 0x840
#define APBDEV_PMC_RST_STATUS_0 0x1B4
#define APBDEV_PMC_SECURE_SCRATCH49_0 0x3A4
#define APBDEV_PMC_SCRATCH49_0 0x244
#endif

View File

@@ -492,7 +492,7 @@ static int _sd_storage_send_if_cond(sdmmc_storage_t *storage)
if (!sdmmc_get_rsp(storage->sdmmc, &resp, 4, SDMMC_RSP_TYPE_5))
return 0;
return (resp & 0xFFF) == 0x1AA ? 1 : 0;
return (resp & 0xFF) == 0xAA ? 1 : 0;
}
static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int is_version_1, int supports_low_voltage)
@@ -518,7 +518,8 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
{
if (cond & 0x40000000)
storage->has_sector_access = 1;
// TODO: Some SD Card incorrectly report low voltage support
// Disable it for now
if (cond & 0x1000000 && supports_low_voltage)
{
//The low voltage regulator configuration is valid for SDMMC1 only.
@@ -537,7 +538,7 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
}
if (get_tmr() > timeout)
break;
sleep(1000);
sleep(10000); // Needs to be at least 10ms for some SD Cards
}
return 0;
@@ -758,6 +759,9 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
case 1:
storage->sec_cnt = (1 + unstuff_bits(csd, 48, 22)) << 10;
break;
case 2:
storage->sec_cnt = (1 + unstuff_bits(csd, 48, 22)) << 10;
break;
default:
DPRINTF("[sd] Unknown CSD structure %d\n", csd_struct);
//TODO: I've encountered this with one of my SD cards, but
@@ -793,7 +797,8 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
memcpy(storage->scr, buf, 8);
DPRINTF("[sd] got scr\n");
if (bus_width == SDMMC_BUS_WIDTH_4 && storage->scr[1] & 4)
// Check if card supports a wider bus and if it's not SD Version 1.0
if (bus_width == SDMMC_BUS_WIDTH_4 && storage->scr[1] & 4 && (storage->scr[0] & 0xF))
{
if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN))
{
@@ -815,7 +820,7 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
}
DPRINTF("[sd] enabled highspeed (low voltage)\n");
}
else if (type != 6 && storage->scr[0] & 0xF != 0)
else if (type != 6 && (storage->scr[0] & 0xF) != 0)
{
if (!_sd_storage_enable_highspeed_high_volt(storage, buf))
{

View File

@@ -54,24 +54,30 @@ int sdmmc_get_voltage(sdmmc_t *sdmmc)
static int _sdmmc_set_voltage(sdmmc_t *sdmmc, u32 power)
{
u8 pwr = 0;
switch (power)
{
case SDMMC_POWER_OFF:
sdmmc->regs->pwrcon &= ~TEGRA_MMC_PWRCTL_SD_BUS_POWER;
break;
case SDMMC_POWER_1_8:
sdmmc->regs->pwrcon =
(sdmmc->regs->pwrcon & TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_MASK) |
TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8;
sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8;
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8;
break;
case SDMMC_POWER_3_3:
sdmmc->regs->pwrcon = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3;
pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3;
break;
default:
return 0;
}
sdmmc->regs->pwrcon |= TEGRA_MMC_PWRCTL_SD_BUS_POWER;
if (power != SDMMC_POWER_OFF)
{
pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER;
sdmmc->regs->pwrcon = pwr;
}
return 1;
}